2014年3月25日星期二

Django:View, UrlMappping,Template

Reference: https://docs.djangoproject.com/en/1.6/intro/tutorial03/ 
【笔记性质,做好的材料还是原文!】

Web page在Django来自于view。view可由Python的函数表示(定义),view也可由方法表示。如果view是由方法而非函数定义,则说这个view是基于类的。Django通过检查URL知道对应的view。URL到view的映射,在Django里被称作URLconfs。URLconf把URL patterns映射到views。URLconf就是对应module目录下的文件urls.py

1) 定义View
官方例子,view的定义如下(views.py):
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")

def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)

上面的两个函数idnex和detail分别定义(或者说指向了)了两个view。

2) 定义URLconf
作为URLconf的文件urls.py的内容如下,
from django.conf.urls import patterns, url
from polls import views

urlpatterns = patterns('',
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P\d+)/$', views.detail, name='detail'),
)

urls.py中的url方法的第二参数就是函数名。
如,views.index,就是views.py中的函数index;  urls.py中的views.detail,就是views.py中的函数detail。
在urls.py中的url方法的第三参数就是视图名称。name='index' name='detail',就是分别定义了view的名字。
url的第一参数定义了reuqest's url到view的匹配规则。

**熟悉例子的时候,因为copy/past而没有修改对应的package名,结果导致在自己的application中,一直在调用无关应用polls的views.index方法。所以,最好不用用views这样的泛泛的名字做文件名,使用YourFeatuherViews这样具体的名称做文件名更好。

3) 把应用的url路由加入到project的路由定义中
最后,在project目录下的urls.py中,加入你创建的application的URLconf:
url(r'^autoInstaller/', include('autoInstaller.urls')),
url(r'^polls/', include('polls.urls')),

4)url到view的映射过程
当请求被Django server接收到,首先有项目的URLconf进行处理,如果找到了与某个应用application对应的url,则按照url()里的定义截取掉匹配的部分,然后继续转给这个应用的URLconf,这个应用再进行匹配处理,比如根据应用的URLconfg来截取、分割url。最后转给与之匹配的view method。 这种处理方式带来一个好处——方便应用的共享、分发。加入你的应用到新的project中时,只需要修改project的URLconfig与你的应用的对应url匹配规则就好了。

5)Template
view 要么返回一个HttpResponse要么抛出Exception

在view里,实际上你可以做很多事情,读数据库记录、生成PDF...但最后总期望的总是HttpResonse、或是Exception。页面显示方面的代码可以直接在view方法里写,当然最好是写到template里。Django的模板loader可以找到你应用下的template目录作为源导入其中的模板。在你应用的目录下建立目录tempaltes,在templates目录下再建立以app名为名字的目录。在这个目录下面保存这个app需要的模板。
下面这个段是模板代码:
{% if latest_poll_list %}
    {% for poll in latest_poll_list %}
           href="/polls/{{ poll.id }}/">{{ poll.question }}
    {% endfor %}
{% else %}      No polls are available. {% endif %}
    
下面这个段是导入模板、渲染模板的代码:
from django.http import HttpResponse
from django.template import RequestContext, loader

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = RequestContext(request, {
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(template.render(context))
context就是把模板变量映射到python对象的字典。在模板里通过{%  %}把python代码扩起来。

可以把上面那段代码通过from django.shortcuts import render 简化成一行: return render(request, 'polls/index.html', context)

|

2014年1月16日星期四

iOS笔记:CocoaPods

吐槽一下:类似Maven、Gradle,当然更像gradle, 很怀疑maven是学院派的人主导的产品(一堆xml让谁看啊?!),gradle才有搞工程人的实际品质。

安装CocoaPods需要有:
1)Ruby 环境。下面的命令更新Ruby环境。
sudo gem update --system
2) Command Line Tools for Xcode

安装CocoaPod:
sudo gem install cocoapods
pod setup
执行sudo gem install cocoapods时会提示:
rake's executable "rake" conflicts with /usr/bin/rake
Overwrite the executable? [yN]  y
Successfully installed rake-10.1.1
选择y覆盖,否则需要设置user的环境变量。Ref: http://guides.cocoapods.org/using/getting-started.html

创建Podfile:
open -e Podfile
platform :iOS
pod ‘AFNetWorking’, ‘0.9'


运行pod install, 抓取Podfile里指定的库到本地。
pod install

用xcode打开pod install生成的workspace文件,而不是原来的project文件。否则会有下面的错误:ld: library not found for -lPods


RubyGems 是什么? 
The RubyGems software allows you to easily download, install, and use ruby software packages on your system. The software package is called a "gem" and contains a package Ruby application or library.

Gems can be used to extend or modify functionality in Ruby applications. Commonly they're used to distribute reusable functionality that is shared with other Rubyists for use in their applications and libraries. Some gems provide command line utilities to help automate tasks and speed up your work.  


尝试Django


已经决定2014在公司自娱的项目,就用python来做了。这些日子熟悉了sublime,Paramiko,tenjin。把一些基本的操作点代码写出来了,如果只是做个命令行的东西,从技术点上该尝试的都已经ok了。

我不喜欢在命令行敲命令去驱动程序,我喜欢在屏幕上输入参数,点击“Run”。。。运行的逻辑在心里,看到的应该只是状态...

下面这篇文章讲了几个python上的web框架,不错。http://feilong.me/2011/01/talk-about-python-web-framework

我选了djiango,应该能省不少事吧。https://www.djangoproject.com/

参考入门教程: https://docs.djangoproject.com/en/1.6/intro/tutorial01/

上面的教程跟当前版本有出入,运行easy_install Sphinx 安装文档生成器。再到Django的doc目录运行命令“make html”来生成HTML文档。


1)创建Djiango Project。
可以把project理解成一个web应用,一个project会包含一些app。
创建一个项目的根目录,运行:“python C:\Python27\Lib\site-packages\django\bin\django-admin.py startproject oesTester”

2)默认的创建出来的project会带有几个app,其中的一些app需要数据库的支持,所以,需要运行“python manage.py syncdb”来在数据库中创建一些表。最后会提示你创建后台管理应用的管理员账号、密码。

3)创建Project里的一个app。
可以把一个app理解成负责某项功能的模块。app的python代码可以在任何python path里。
python C:\Python27\Lib\site-packages\django\bin\django-admin.py startproject oesTester
一个project有多个apps组成。在DJango中model是data class的意思,对应于ORM里的数据模型。
下面的命令创建一个app,名称是"autoInstaller"。
python manage.py startapp autoInstaller

启动Web Server: python manage.py runserver

4) 创建model的步骤略去,因为这个项目第一版不准备使用DB.


5) 访问 ,页面没有加载css,开始一直以为是Django static的配置问题。后来注意到console有“mimetypes.py", line 249, in enum_types”之类的信息,原来这是python2.7的一个bug导致的。
   修改mimetypes.py,注释掉下面这些行
                #try:
                #    ctype = ctype.encode(default_encoding) # omit in 3.x!
                #except UnicodeEncodeError:
                #    pass



2013年12月27日星期五

SublimeText的插件安装

最后还是同时装上了SublimeText2和3。

SublimtText用来安装插件的package controller插件,需要从console安装。ctrl+`
如果你系统使用python2,使用下面语句:
import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print('Please restart Sublime Text to finish installation') 
如果你系统使用python3,使用下面语句:
import urllib.request,os;pf = 'Package Control.sublime-package';ipp = sublime.installed_packages_path();urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()));open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())

如果,你要通过代理上网并且不是windows操作系统(Windows操作系统使用IE的代理配置),需要把代理配置写到urllib2.ProxyHandler()里,比如:
urllib2.ProxyHandler({'http':'cn-proxy.abc.com:80'})

安装pylint,需要你的python环境里有lint.py, 如果你没有lint.py, 就先安装pip, easy-setup什么的,参照这帖子:http://stackoverflow.com/questions/4750806/how-to-install-pip-on-windows

安装完pylint,还要把lint.py的路径写到pylint的用户配置文件,比如"pylint_path": "C:\\Python27\\Lib\\site-packages\\pip\\vendor\\html5lib\\filters"


2013年9月30日星期一

Oracle ADG 环境搭建

Oracle官方文档

不错的网络文章

《Oracle Data Guard理论知识》 这篇文章对于理解DataGuard如何利用log进行了介绍。
《Data Guard broker系列》《Data Guard Physical Standby Setup in Oracle Database 11g Release 2》 这篇文章有坑(没有提及_DBG, _DGMGRL),但总体顺序不错。
《Four Steps to create a Physical Standby Database》  这篇文章轮廓写得清晰。 上篇文章中没有提及listener.ora文件中的_DGB , _DGMGRL这两个static listener,是绝对的坑。 _DGB是Datagurad Broker用来侦听不同节点心跳的。_DGMGRL是用来保证数据库down的时候能够访问的。

《How to add and remove databases from Oracle cluseter》这篇主要示例了如何使用crvctl。

 

安装中遇到的一些问题&解决:

1)
现象:
SQL>  STARTUP NOMOUNT PFILE=’/tmp/initDB11G_stby.ora’;
ORA-01261: Parameter db_recovery_file_dest destination string cannot be translated
ORA-01262: Stat failed on a file destination directory
Linux-x86_64 Error: 2: No such file or directory
SQL>
解决:
因为在standby上创建的fast_recovery_area时,把一个字母写错了…

2)
现象:
Warning: ORA-16714: the value of property ArchiveLagTarget is inconsistent with the database setting
解决:
先查看那些不一致,然后更正之。
DGMGRL>  show database 'db11g_stby' 'InconsistentProperties';
edit database 'db11g_stby' set property 'ArchiveLagTarget'=0;
可参考官方文档 http://docs.oracle.com/cd/E11882_01/server.112/e17023/cli.htm
3)
现象:
分别进入primary sqlplus , standby sqlplus执行—-》
SQL> select flashback_on from v$database;
SQL> alter database flashback on;
在standby 上
SQL> alter database flashback on;
alter database flashback on
*
ERROR at line 1:
ORA-01153: an incompatible media recovery is active
SQL>
解决:SQL> STARTUP MOUNT;
ORACLE instance started.
Total System Global Area 2421825536 bytes
Fixed Size 2228880 bytes
Variable Size 1358957936 bytes
Database Buffers 1040187392 bytes
Redo Buffers 20451328 bytes
Database mounted.
SQL> alter database flashback on;
Database altered.
4)
现象:switchover失败
DGMGRL> switchover to db11g_stby;
Performing switchover NOW, please wait…
Error: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
Error: ORA-16625: cannot reach database “db11g_stby”
Failed.
Unable to switchover, primary database is still “db11g”
DGMGRL> show configuration;
Configuration – myconfigProtection Mode: MaxAvailability
Databases:
db11g      – Primary database
db11g_stby – Physical standby databaseFast-Start Failover: DISABLED
Configuration Status:
ORA-16623: database detected role change
ORA-16625: cannot reach database “db11g”
DGM-17017: unable to determine configuration status
DGMGRL>
解决:查看一下stby数据库broker参数,发现staticConnectorIdentifier默认使用1521端口。而我用的stby数据库是1522端口。
DGMGRL> show database  verbose ‘db11g_stby’;
RecvQEntries                    = ‘(monitor)’
SidName                         = ‘DB11G’
StaticConnectIdentifier         = ‘(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=slc01bme)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=DB11G_STBY_DGMGRL.us.oracle.com)(INSTANCE_NAME=DB11G)(SERVER=DEDICATED)))’
StandbyArchiveLocation          = ‘USE_DB_RECOVERY_FILE_DEST’
可以通过一下命令之一修改之:
//通过dgmgrl:
edit database db11g_stby set property StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=slc01bme)(PORT=1522))(CONNECT_DATA=(SERVICE_NAME=DB11G_STBY_DGMGRL.us.oracle.com)(INSTANCE_NAME=DB11G)(SERVER=DEDICATED)))';
 或者 sqlplus:
alter system set StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=slc01bme)(PORT=1522))(CONNECT_DATA=(SERVICE_NAME=DB11G_STBY_DGMGRL.us.oracle.com)(INSTANCE_NAME=DB11G)(SERVER=DEDICATED)))' scope=both;
参考:
《解决Oracle DataGuard Broker出现的TNS-12514错误》《Four steps to create a Physical Standby Database》 这篇文章里,是在添加了_DBG, _DGMGRL两个服务名之后,执行诸如如下的命令来确保提供合适的conenction给DG broker。下面的命令因为使用服务名而优于前文的链接字符串。
edit database db11g set property staticConnectidentifier=’DB11G’;
edit database db11g_DG1 set property staticConnectidentifier=’DB11G_DG1′;
5)
现象:-bash-3.2$ srvctl add ons
PRCR-1063 : Failed to create context
PRKH-1010 : Unable to communicate with CRS services.
PRKH-1000 : Unable to load the SRVM HAS shared library
PRKN-1008 : Unable to load the shared library “libsrvmhas11.so”
or a dependent library, from
LD_LIBRARY_PATH=”/scratch/dahyuan/app/oracle/product/11.2.0/grid/lib”
[java.lang.UnsatisfiedLinkError: /scratch/dahyuan/app/oracle/product/11.2.0/grid/lib/libsrvmhas11.so: libasmclnt11.so: cannot open shared object file: No such file or directory]
解决:
一种是你的server上真的缺少一些开发包,需要你编译安装。但我这里是因为LD_LIBRARY_PATH的问题,当时libary path没有/usr/lib和/usr/local/lib这两个目录。加上之后再运行root.sh就好了。 LD_LIBRARY_PATH应该至少包括/scratch/dahyuan/app/oracle/product/11.2.0/grid/lib:/usr/lib:/usr/local/lib。
参考:
《Oracle 安装 clusterware时遇到的问题》
6)
现象:-bash-3.2$ srvctl start ons
#
# An unexpected error has been detected by HotSpot Virtual Machine:
# SIGSEGV (0xb) at pc=0x00002ada0b2b3a84, pid=2082, tid=47115974751584
# Java VM: Java HotSpot(TM) 64-Bit Server VM (1.5.0_24-rev-b08 mixed mode)
# Problematic frame:
# V [libjvm.so+0x3afa84]
#
# An error report file with more information is saved as hs_err_pid2082.log
#
# If you would like to submit a bug report, please visit:
#
/scratch/dahyuan/app/oracle/product/11.2.0/dbhome_1/bin/srvctl: line 240: 2082 Aborted $JRE $JRE_OPTIONS -DORACLE_HOME=$ORACLE_HOME -classpath $CLASSPATH $SRVM_PROPERTY_DEFS oracle.ops.opsctl.OPSCTLDriver “$@”
解决:
这个是因为使用了Database_HOME里的srvctl。应该使用grid_HOME/bin里的srvctl。
参考:
《SRVCTL文档》  To manage Oracle ASM on Oracle Database 11g release 2 (11.2) installations, use the SRVCTL binary in the Oracle Grid Infrastructure home for a cluster (Grid home). If you have Oracle RAC or Oracle Database installed, then you cannot use the SRVCTL binary in the database home to manage Oracle ASM.

7)
现象:
DGMGRL>  ENABLE FAST_START FAILOVER;
Error: ORA-16651: requirements not met for enabling fast-start failover
解决:
在primary 和 standby上用sqlplus 都执行,  alter database flashback on;


8)
现象:
DGMGRL> show database  verbose 'db11g_stby';
出现以下警告,
  Database Warning(s):

    ORA-16826: apply service state is inconsistent with the DelayMins property
解决:
在DGMGRL里执行,disable configuration;    再执行 enable configuration;  就可以解决。



2013年1月14日星期一

【翻译】The “reassociation” business of Fusion Middleware

原文出处:http://fusionsecurity.blogspot.jp/2011/10/reassociation-business.html 下面大致翻译了一下,不精准,笔记性质。

 从Fusion Middleware 11.1.1.4开始,OPSS(Oracle Platform Security Service)支持三种类型的安全存储(Security Store):文件、OID(Oracle Internet Directory)以及Oracle DataBase。当weblogic server的domain刚被创建出来时,默认地,OPSS是和文件类型的存储相关联的。文件存储比较适合开发阶段。对于生产环境,不推荐文件存储类型(请参考“ Multiple Nodes Servers Environments”)。如果你的运行环境是在一个台机器上只有唯一的一个weblogic domain、一个weblogic server,文件类型存储也是能工作的。但是99.99%的情况,你的SOA、WebCenter环境是运行在集群环境下的多个服务器上。文件存储不提供这种伸缩性,这种情况下,你应该考虑OID或者Oracle DataBase。

实际上,很多的Fusion应用时使用OID。:) OPSS安全存储由policy,credential,keys以及audit services组成。注意:我没有提及identity store service。因为,OPPS把identity store service'委托给了在weblogic server里配置的'identity provider'。 OPSS不是一个产品,它是Fusion Middleware使用的一组安全服务。 这个帖子十分详细地讲述了如何配置weblogic server domain使用以上的三种不同security store。这也是"reassociation(关联)"这个词的来历。 下面的内容和 “Configuring OPSS Security Store”(强烈推荐阅读)有些重叠。


 在更进一步讲述association之前,我们先说说jps-config.xml的关键点。

jps-config.xml
OPSS使用这个文件描述它所提供的服务。通过设置系统属性–Doracle.security.jps.config你可以指定OPSS从哪里读取这个文件,通常在JRF(Java Required Files)domain的setDomainEnv.sh里设置这个文件的位置。
缺省的位置是${DOMAIN_HOME}/config/fmwconfig/jps-config.xml。(定义在变量EXTRA_JAVA_PROPERTIES里)。建议不要轻易修改jps-config.xml,因为jps-config.xml包含了许多对其它文件的引用。 当以通过config.sh脚本来创建BPM,SOA或者WebCenter 域(domain)时,JRF template会自动被选中,因为这些鱼都依赖JRF。 jps-config.xml是作用于域范围的配置文件。并不存在针对server或者application这一层起作用的jps-config.xml文件。但是,jps-config.xml里context的概念,可以让你让不同的application使用不同的安全服务。这是另外一个话题了。 当执行了reassociation操作,配置的改变会被写入jps-config.xml。破损的jps-config.xml会让你的domain运行在不稳定的状态。所以,对jps-config.xml的修改要慎之又慎。不要手工修改这个文件,而是要用Enterprise Manager或者wlst。

  The Policy Store Policy store保存着Fusion Middleware实例上运行的application的所需要的全部安全策略,包括赋予pricials(users,groups, application roles)以及code 的允许权限。 比如,如果你查看BPM域上OOTB的policy store,你就会发现这些策略分成四组,供4个应用 (OracleBPMProcessRolesApp, OracleBPMComposerRolesApp, b2bui and soainfra)使用。另外还有一组对部署在这个域的所有应用都生效的code-source策略。 jps-config.xml文件里的default context定义了Fusion Middleware里所有应用都用到的服务。

The Credential Store Credential store保存着Fusion Middleware应用连接到其它系统所用的凭证。这不同于下面要讲到的boostrap credentials。 File Based Credential Store Service Instance 

Reassociating to OID 有两种方式做reassociation。 Enterprise Manager和wlst。(只大致介绍一下关键参数的含义,具体见原文。)
EM界面里的: Root DN: 这个需要再做reassociation之前在OID里创建。 Create New Domain: 这里不是表示“创建一个新域”的意思。这里的意识是说,要关联的OID是不是要要从源security store导入数据进行初始化。不选择这个checkbox意味着,你有多个domain共享这个security store。 Domain Name: 这里方便起见,EM使用weblogic域名。但实际上,它可以是任意名字。这个名字用于标识它为一个container节点。多个weblogic域可以绑定到相同的container,但是,一个weblogic域不能绑订到不同的container。

wlst里的参数: > reassociateSecurityStore(domain="farm1", admin="cn=orcladmin", password="welcome1", ldapurl=ldap://localhost:3060, servertype="OID", jpsroot="cn=SecurityStore",[join=”trueOrfalse”]) 理解了EM的输入,wlst就容易理解了。join这个参数,对应着EM里的是否'Create new domain'。

Bootstrap cwallet.sso 
当reassociated到OID时,weblogic需要知道连接到OID服务器的登录凭证。缺省地,这些凭证被保存在jps-config.xml bootstrap.credstore指定的位置。 如果以后需要修改这个credentials,可以使用以下wlst命令: > modifyBootStrapCredential(jpsConfigFile='',username='', password='') 这里 jpsConfigFile = 有效的jps config文件路径。 username = 用户名 password = 新密码 比如 > modifyBootStrapCredential(jpsConfigFile='/opt/wls/oracle/middleware/user_projects/domains/soa_domain/config/fmwconfig/jps-config.xml',username='cn=orcladmin', password='welcome1')

2012年9月11日星期二

(ZT) MAC 常用软件

Designer Daily的Mirko整理分享的一份免费Mac软件列表,大多数适用于自由职业者。当然,这个概念很模糊,没有什么软件是只有自由职业者才使用的,自由职业者本身又是一个包罗万象、异常模糊的概念。 所以, 所有Mac用户都可以看看,Windows用户也可以看,因为Mirko同时使用Mac和Windows,他不但列出了免费Mac软件,还包括相关的Windows平台下的替换方案。 1、Name Changer 重命名多个文件,可以完美地一次给多个文件重新命名。当你处理图片的时候,它是你的最佳助手。 2、Anxiety 这款轻量级的软件可以帮助你管理简单的任务列表。如果你不想用复杂的GTD软件,那么这款软件最适合你。 3、Cyberduck 拥有漂亮界面的FTP软件,并且整合了多个文本编辑器。 4、Adium 这款开源的即时通讯软件可以让你登陆所有的IM账号。 5、Carbon Copy cloner 帮助你快速建立一个可以引导启动的磁盘镜像。 6、The Unarchiver 兼容多种模式的解压缩软件。 7、AppCleaner 帮助你在删除软件的时候不留下任何垃圾文件。 8、The Gimp 图像处理软件,photoshop的替代品。 9、Skype Windows下也可以使用 10、firefox 免费、开源的浏览器,拥有大量插件。 Windows下也可以使用 11、Caffeine 不要让你的Mac休眠,给它点咖啡因吧。 12、Burn 高级CD和DVD刻录软件。 13、Handbrake DVD到Mpeg格式的转换软件,可以帮助你转换DVD影片。 Windows下也可以使用 14、Audacity 开源的录音、音频编辑软件。 Windows下也可以使用 15、Smultron Mac下强大的文本编辑器。 Windows替代方案 16、InkScape 开源的矢量编辑软件,Illustrator的替代品。 Windows也可以使用 17、Freemind 脑图软件,我最喜欢的整理思维的方法。 Windows下也可以使用 18、Disk Inventory X Mac OS X下的磁盘检查工具,帮助你快速查看是什么占用了磁盘空间。 Windows替代方案 19、Colloquy 高级IRC客户端,拥有良好的用户界面。 Windows替代方案 20、Neo Office 开源的办公软件套装,Word和Excel的替代品。 Windows替代方案 21、QuickSilver 快速运行程序的软件,真正提高你的效率。 Windows替代方案 22、NetNewsWire 我最喜欢的RSS阅读器,每天早晨喝咖啡的时候打开。 Windows替代方案 23、DeepVacuum 帮助你通过http或ftp协议下载完整的网站或页面。 Windows替代方案 24、HyperDither 批量修改图片尺寸的软件,如果你不喜欢用Photoshop的动作,这款软件可以为你节省时间。 Windows替代方案 25、Transmission 这款BT软件让你分享文件变得非常简单。 Windows替代方案 英文原文: 25 free Mac Apps for freelancers 中文译文: yeeyan