diff --git a/README.md b/README.md index 67d27c9..0969629 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,22 @@ Linux (or cygwin) with php & curl installed. ## 使用方法 - chmod +x bpcs_uploader.php + chmod +x bpcs_uploader.php ./bpcs_uploader.php -由于VPS上安装的php可能存在于各种地方,因此运行很可能不正常。请以使用`which php`得到你的php绝对路径,修改bpcs_uploader.php的头一句#!后的路径。 -如果你的php是为了网站环境安装的,那么很有可能你会得到下面这条错误消息: +由于VPS上安装的php可能存在于各种地方,因此运行很可能不正常。请以使用`which php`得到你的php绝对路径,修改bpcs_uploader.php的头一句#!后的路径。 +如果你的php是为了网站环境安装的,那么很有可能你会得到下面这条错误消息: -> xxx() has been disabled for security reasons +> xxx() has been disabled for security reasons -那么说明你的环境由于安全原因禁止了部分函数的执行。请看FAQs的1。请使用那条长长的命令代替./bpcs_uploader.php。eg:`php -d disable_functions -d safe_mode=Off -f bpcs_uploader.php quota` +那么说明你的环境由于安全原因禁止了部分函数的执行。请看FAQs的1。请使用那条长长的命令代替./bpcs_uploader.php。eg:`php -d disable_functions -d safe_mode=Off -f bpcs_uploader.php quota` ### 快速初始化 ./bpcs_uploader.php quickinit 敲下命令直接进入快速初始化流程,输入y,然后打开浏览器访问 https://openapi.baidu.com/device ,在“请输入设备上显示的用户授权码:”文本框中输入上面显示的授权码(这里是`12abcxyz`),并点击继续。 -看到网页上显示“请返回设备继续操作!”后,返回ssh上按下回车后,即完成了初始化配置。 +看到网页上显示“请返回设备继续操作!”后,返回ssh上按下回车后,即完成了初始化配置。 ### 初始化 @@ -32,82 +32,85 @@ Linux (or cygwin) with php & curl installed. 敲下命令之后会进入初始化流程,这里分段详述设置方法。 -> Now start the initiation. If you have configured the uploader , it will be overwirte. +> Uploader initialization will be begin. If you have already configured +> the uploader before, your old settings will be overwritten. > Continue? [y/N] y -确认初始化。如果之前有初始化过,那么以前的配置将会被覆盖。 - -> Now you have to enter your baidu PSC app key . You should know that it needs a manual acting. -> You can request for it via http://developer.baidu.com/dev#/create . -> Make sure you have the PCS app key . if you haven't , you can use the demo key by just hit Enter. -> So if you dont have the app secret , you have to re-init every month , for the access-token will expires every month. -> App KEY [uFBSHEwWE6DD94SQx9z77vgG] : +确认初始化。如果之前有初始化过,那么以前的配置将会被覆盖。 -第一步,输入App key。这里需要输入一个有PCS权限的API KEY,如果没有的话直接敲回车就好了,这里会默认使用内置的一组app key,app secret和app foloder name,所以只要敲下回车就能直接跳到第四步。如果你没有App secret(例如使用L6g70tBRRIXLsY0Z3HwKqlRE这个Key时),只能获取一个有效期为一个月的access token。如果有一个有PSC权限的API KEY和secret,那么就能获得一个有效期为10年的refresh token,以便长期使用。 +> Please enter your PSC App API Key. You can get this key by visiting +> http://developer.baidu.com/dev#/create +> If you already created an app, you can visit http://developer.baidu.com/console#/app and get it in your app's info. +> If you don't want to bother creating an app, you can press Enter to use the demo API Key. +> Doing so (without your own API Key/Secret) will cause the access-token expire every 30 days, and you'll have to +> re-initialize when it expires. +> App API KEY [uFBSHEwWE6DD94SQx9z77vgG] : -> App key has been setted to uFBSHEwWE6DD94SQx9z77vgG . -> Now you have to enter your baidu PSC app secret. If you dont know the secret , keep it blank. -> App SECRET [] : +第一步,输入App key。这里需要输入一个有PCS权限的API KEY,如果没有的话直接敲回车就好了,这里会默认使用内置的一组app key,app secret和app folder name,所以只要敲下回车就能直接跳到第四步。如果你没有App secret(例如使用L6g70tBRRIXLsY0Z3HwKqlRE这个Key时),只能获取一个有效期为一个月的access token。如果有一个有PSC权限的API KEY和secret,那么就能获得一个有效期为10年的refresh token,以便长期使用。 + +> App API Key has been set to uFBSHEwWE6DD94SQx9z77vgG . +> Please enter your Baidu PSC App API Secret. If you have no idea what it is, keep it blank. +> App API SECRET [] : 第二步,输入App secret。如果输入了app secret,将会转到device code模式验证;或者直接输入回车使用oob模式验证。先直接回车: -> Now you have to enter your app name. You can enter it later in the file [ /root/_bpcs_files_/config/appname ]. -> * Why i have to enter app name ? see FAQs. -> If your app name have Chinese characters , please swith your client to the UTF-8 mode. -> Here are some chinese characters . Before you enter chinese characters , make sure you can read these characters. -> 这里是一些中文字符。 -> If you cant read any chinese above , please press enter , and change it manually in the file [ /root/_bpcs_files_/config/appname ] . -> If you have Enter the key [L6g70tBRRIXLsY0Z3HwKqlRE] (by default) , just press Enter. -> App Name [pcstest_oauth] : - -第三步,这里需要输入app floder name,也就是你申请API时填写的文件夹名字。详情见FAQ 2。因为是使用的默认的key,所以直接回车即可。 - -> Now you have to get your oauth access_token by your own . -> Here is a reference document . -> http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/usage_example -> -> A simple guide : -> 1.visit https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=L6g70tBRRIXLsY0Z3HwKqlRE&redirect_uri=oob&scope=netdisk -> in your broswer. -> 2.when it redirected to a html page , copy the url to the notepad. -> 3.get the access_token from it , paste it and press Enter. -> access_token[] : - -第四步,获取access token。在浏览器中打开上述URL( https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=L6g70tBRRIXLsY0Z3HwKqlRE&redirect_uri=oob&scope=netdisk ),进行授权。 +> Please enter your app's folder name. You can choose to input this later in the file [ /root/_bpcs_files_/config/appname ]. +> ** Why do I have to enter the app's folder name? Please check the FAQs. ** +> If your app's name has Chinese characters, please ensure that your client supports UTF-8 encoding. +> Below are some Chinese characters for testing. Please make sure that you can read them before you enter Chinese here. +> 这里是一些中文字符。 +> If you can't read the characters above, please press Enter and change it manually within the file [ /root/_bpcs_files_/config/appname ]. +> If you have Enter the key [L6g70tBRRIXLsY0Z3HwKqlRE] (by default) , just press Enter. +> App Name [pcstest_oauth] : + +第三步,这里需要输入app folder name,也就是你申请API时填写的文件夹名字。详情见FAQ 2。因为是使用的默认的key,所以直接回车即可。 + +> In the next step, you'll have to grab the access_token generated by Baidu. +> You can check out this link for more information on this procedure. +> http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/usage_example +> +> Easy Guide: +> 1. Visit https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=$appkey&redirect_uri=oob&scope=netdisk +> 2. After the page is being redirected (it should show something like OAuth 2.0), copy the current URL to your favorite text editor. +> 3. Grab the access_token part, take only the part between "access_token=" and the next "&" symbol (without quotes). +> 4. Copy it and paste here, then press Enter. +> access_token[] : + +第四步,获取access token。在浏览器中打开上述URL( https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=L6g70tBRRIXLsY0Z3HwKqlRE&redirect_uri=oob&scope=netdisk ),进行授权。 授权完毕后,将会跳到一个写着“百度 Oauth2.0”的页面。复制出其中的网页URL,找到access_token=和&之间的字符串,例如: -`3.**05c2ea85d52c2***************a5.2592000.136***9032.3089166538-23**47` -将其复制到shell中粘贴并回车。使用这种方式初始化的用户,需要每月重新初始化。 +`3.**05c2ea85d52c2***************a5.2592000.136***9032.3089166538-23**47` +将其复制到shell中粘贴并回车。使用这种方式初始化的用户,需要每月重新初始化。 如果第三步输入app secret的时候没有留空,将会得到下面的消息: -> Now open your broswer and visit https://openapi.baidu.com/device . -> Copy or input 12abcxyz when it been asks. -> After granted the access to the application , be back and press Enter key . +> Launch your favorite web browser and visit https://openapi.baidu.com/device +> Input 12abcxyz as the user code if asked. +> After granting access to the application, come back here and press Enter to continue. 来到这里,打开浏览器访问 https://openapi.baidu.com/device ,在“请输入设备上显示的用户授权码:”文本框中输入上面显示的授权码(这里是`12abcxyz`),并点击继续。 -看到网页上显示“请返回设备继续操作!”后,返回ssh上按下回车后,即可继续。 +看到网页上显示“请返回设备继续操作!”后,返回ssh上按下回车后,即可继续。 -> curl -X GET -k -L "...." -> % Total % Received % Xferd Average Speed Time Time Time Current -> Dload Upload Total Spent Left Speed -> 0 62 0 62 0 0 40 0 --:--:-- 0:00:01 --:--:-- 235 -> Success . Your Storage Status : 0.06G/115.00G (0.05%) -> Have fun ! +> curl -X GET -k -L "...." +> % Total % Received % Xferd Average Speed Time Time Time Current +> Dload Upload Total Spent Left Speed +> 0 62 0 62 0 0 40 0 --:--:-- 0:00:01 --:--:-- 235 +> Access Granted. Your Storage Status: 0.06G/115.00G (0.05%) +> Enjoy! 你所看到的输出可能和这里给出的不一样,但是只要看到了存储空间的剩余量,和【Have fun !】提示,即说明成功初始化。 ### 查询容量(配额) - ./bpcs_uploader.php quota + ./bpcs_uploader.php quota -结果: +结果: > Your Storage Status : 0.06G/115.00G (0.05%) ### 上传文件 ./bpcs_uploader.php upload [path_local] [path_remote] -路径格式:`foo/bar/file.ext`(路径中一定要包括文件名) +路径格式:`foo/bar/file.ext`(路径中一定要包括文件名) 上传后,能在百度网盘/我的应用数据/应用名/foo/bar下找到一个叫file.ext的文件。 ### 下载文件 @@ -126,10 +129,10 @@ Linux (or cygwin) with php & curl installed. ## FAQs -1. 各种错误提示 +1. 各种错误提示 -试试`php -d disable_functions -d safe_mode=Off -f bpcs_uploader.php`。 +试试`php -d disable_functions -d safe_mode=Off -f bpcs_uploader.php`。 -2. 为什么要输入文件夹名? +2. 为什么要输入文件夹名? 因为百度PCS的权限被限制在了/apps/文件夹名/下。如果发现输入文件夹名后仍然无法上传文件,请通过网页版找到【我的应用数据】找到对应的文件夹名,写入/config/appname文件。上传文件的时候会自动帮您处理文件夹,无需手动写出完整路径。 diff --git a/_bpcs_files_/core.php b/_bpcs_files_/core.php index 148c463..47c20c8 100644 --- a/_bpcs_files_/core.php +++ b/_bpcs_files_/core.php @@ -1,227 +1,228 @@ $access_token, - 'refresh_token' => $refresh_token, - ); - } - function do_oauth_token($appkey){ - echo << $access_token, + 'refresh_token' => $refresh_token, + ); +} +function do_oauth_token($appkey){ + echo << $access_token, - 'refresh_token' => $refresh_token, - ); - } - function get_quota($access_token){ - $quota=do_api('https://pcs.baidu.com/rest/2.0/pcs/quota',"method=info&access_token=".$access_token,'GET'); - $quota=json_decode($quota,1); - apierr($quota); - return $quota; - } - function upload_file($access_token,$path,$localfile,$ondup='newcopy'){ - $path = getpath($path); - $url = "https://c.pcs.baidu.com/rest/2.0/pcs/file?method=upload&access_token=$access_token&path=$path&ondup=$ondup"; - $add = "--form file=@$localfile"; - $cmd = "curl -X POST -k -L $add \"$url\""; - $cmd = cmd($cmd); - $cmd = json_decode($cmd,1); - apierr($cmd); - return $cmd; - } - function delete_file($access_token,$path){ - $path = getpath($path); - $dele=do_api('https://pcs.baidu.com/rest/2.0/pcs/file',"method=delete&access_token=".$access_token.'&path='.$path,'GET'); - $dele=json_decode($dele,1); - apierr($dele); - return $dele; - } - function fetch_file($access_token,$path,$url){ - $path = getpath($path); - $fetch=do_api('https://pcs.baidu.com/rest/2.0/pcs/services/cloud_dl',"method=add_task&access_token=".$access_token.'&save_path='.$path.'&source_url='.$url,'GET'); - $fetch=json_decode($fetch,1); - apierr($fetch); - return $fetch; - } - //分片上传 - function super_file($access_token,$path,$localfile,$ondup='newcopy',$sbyte=1073741824,$temp_dir='/tmp/'){ - //调用split命令进行切割 - //split -b200 --verbose rubygems-1.8.25.zip rg/rg1 - if(filesize($localfile)<=$sbyte){ - echon('The file is not as big as it need to be created by superfile.'); - upload_file($access_token,$path,$localfile,$ondup); //直接上传 - } - $tempfdir = rtrim($temp_dir,'/').'/'.uniqid('bpcs_to_upload_'); - if(!mkdir($tempfdir,0700,true)){ - echon('Cannot create temp dir:'.$tempfdir); - die(9009); - } - $splitcmd = "split -b{$sbyte} $localfile $tempfdir/bpcs_toupload_"; - $splitresult = cmd($splitcmd); - if(trim($splitresult)){ - echon('Split quit with an message:'.$splitresult); - } - //遍历临时文件目录 - $tempfiles = glob($tempfdir.'/bpcs_toupload_*'); - if(count($tempfiles)<1){ - //没有生成文件 - echon('There are no files to upload.'); - die(9010); - }elseif(count($tempfiles)==1){ - //只有一个文件 - unlink($tempfiles[0]); //删除它 - echon('The file is not as big as it need to be created by superfile.'); - upload_file($access_token,$path,$localfile,$ondup); //直接上传 - return; - } - //开始上传进程 - $block_list = array(); - $count = 0; - foreach($tempfiles as $tempfile){ - //上传临时文件,上传API与上传普通文件无异,只是多一个参数type=tmpfile,取消了其它几个参数。此处将“&type=tmpfile”作为ondup传递,将参数带在请求尾部。 - echon('Uploading '.($count+1).' out of '.count($tempfiles).' file blocks ... '); - $count++; - $upload_res = upload_file($access_token,'',$tempfile,$ondup.'&type=tmpfile'); - $block_list[] = $upload_res['md5']; - //删除临时文件 - unlink($tempfile); - } - //删除临时文件夹 - rmdir($tempfdir); - //准备提交API - $block_list = json_encode($block_list); - $param = '{"block_list":'.$block_list.'}'; - $param = 'param='.urlencode($param); - $path = getpath($path); - $url = "https://pcs.baidu.com/rest/2.0/file?method=createsuperfile&path={$path}&access_token={$access_token}"; - $res = do_api($url,$param); - } \ No newline at end of file + echo 'access_token[] : '; + $access_token = getline(); + return $access_token; +} +function do_oauth_refresh($appkey,$appsec,$refresh_token){ + $para = 'grant_type=refresh_token&refresh_token='.$refresh_token.'&client_id='.$appkey.'&client_secret='.$appsec; + $token_json = do_api('https://openapi.baidu.com/oauth/2.0/token',$para); + $token_array = json_decode($token_json,1); + $access_token = $token_array['access_token']; + $refresh_token = $token_array['refresh_token']; + return array( + 'access_token' => $access_token, + 'refresh_token' => $refresh_token, + ); +} +function get_quota($access_token){ + $quota=do_api('https://pcs.baidu.com/rest/2.0/pcs/quota',"method=info&access_token=".$access_token,'GET'); + $quota=json_decode($quota,1); + apierr($quota); + return $quota; +} +function upload_file($access_token,$path,$localfile,$ondup='newcopy'){ + $path = getpath($path); + $url = "https://c.pcs.baidu.com/rest/2.0/pcs/file?method=upload&access_token=$access_token&path=$path&ondup=$ondup"; + $add = "--form file=@$localfile"; + $cmd = "curl -X POST -k -L $add \"$url\""; + $cmd = cmd($cmd); + $cmd = json_decode($cmd,1); + apierr($cmd); + return $cmd; +} +function delete_file($access_token,$path){ + $path = getpath($path); + $dele=do_api('https://pcs.baidu.com/rest/2.0/pcs/file',"method=delete&access_token=".$access_token.'&path='.$path,'GET'); + $dele=json_decode($dele,1); + apierr($dele); + return $dele; +} +function fetch_file($access_token,$path,$url){ + $path = getpath($path); + $fetch=do_api('https://pcs.baidu.com/rest/2.0/pcs/services/cloud_dl',"method=add_task&access_token=".$access_token.'&save_path='.$path.'&source_url='.$url,'GET'); + $fetch=json_decode($fetch,1); + apierr($fetch); + return $fetch; +} +//分片上传 +function super_file($access_token,$path,$localfile,$ondup='newcopy',$sbyte=1073741824,$temp_dir='/tmp/'){ + //调用split命令进行切割 + //split -b200 --verbose rubygems-1.8.25.zip rg/rg1 + if(filesize($localfile)<=$sbyte){ + echon('The file isn\'t big enough to split up. Proceed to upload normally.'); + upload_file($access_token,$path,$localfile,$ondup); //直接上传 + } + $tempfdir = rtrim($temp_dir,'/').'/'.uniqid('bpcs_to_upload_'); + if(!mkdir($tempfdir,0700,true)){ + echon('Cannot create temp dir:'.$tempfdir); + die(9009); + } + $splitcmd = "split -b{$sbyte} $localfile $tempfdir/bpcs_toupload_"; + $splitresult = cmd($splitcmd); + if(trim($splitresult)){ + echon('Split exited with message:'.$splitresult); + } + //遍历临时文件目录 + $tempfiles = glob($tempfdir.'/bpcs_toupload_*'); + if(count($tempfiles)<1){ + //没有生成文件 + echon('There are no files to be upload.'); + die(9010); + }elseif(count($tempfiles)==1){ + //只有一个文件 + unlink($tempfiles[0]); //删除它 + echon('The file isn\'t big enough to split up. Proceed to upload normally.'); + upload_file($access_token,$path,$localfile,$ondup); //直接上传 + return; + } + //开始上传进程 + $block_list = array(); + $count = 0; + foreach($tempfiles as $tempfile){ + //上传临时文件,上传API与上传普通文件无异,只是多一个参数type=tmpfile,取消了其它几个参数。此处将“&type=tmpfile”作为ondup传递,将参数带在请求尾部。 + echon('Uploading file in pieces, '.($count+1).' out of '.count($tempfiles).' parts... '); + $count++; + $upload_res = upload_file($access_token,'',$tempfile,$ondup.'&type=tmpfile'); + $block_list[] = $upload_res['md5']; + //删除临时文件 + unlink($tempfile); + } + //删除临时文件夹 + rmdir($tempfdir); + //准备提交API + $block_list = json_encode($block_list); + $param = '{"block_list":'.$block_list.'}'; + $param = 'param='.urlencode($param); + $path = getpath($path); + $url = "https://pcs.baidu.com/rest/2.0/file?method=createsuperfile&path={$path}&access_token={$access_token}"; + $res = do_api($url,$param); +} diff --git a/bpcs_uploader.php b/bpcs_uploader.php index fac7c2e..cc189d9 100644 --- a/bpcs_uploader.php +++ b/bpcs_uploader.php @@ -1,116 +1,116 @@ -#!/usr/bin/php -d disable_functions -d safe_mode=Off +#!/usr/bin/php -d disable_functions -d safe_mode=Off 1G) - $argv[4] = 1073741824; - //因为需要继续下面的操作所以这里没有break - case 5: //设置默认值(临时文件目录->/tmp/) - $argv[5] = '/tmp/'; - //因为需要继续下面的操作所以这里没有break - default: //开始上传操作 - super_file($access_token,$argv[3],$argv[2],'newcopy',$argv[4],$argv[5]); - } - } \ No newline at end of file +if(!is_dir(CONFIG_DIR)){ + mkdir(CONFIG_DIR); +} +if(!is_file(CONFIG_DIR.'/config.lock') || $argv[1] == 'init' || $argv[1] == 'quickinit'){ + //进行初始化 + echon('Uploader initialization will be begin. If you have already configured the uploader before, your old settings will be overwritten.'); + continueornot(); + du_init($argv[1] == 'quickinit'); + file_put_contents(CONFIG_DIR.'/config.lock',time()); + die(); +} +$access_token = file_get_contents(CONFIG_DIR.'/access_token'); +$refresh_token = file_get_contents(CONFIG_DIR.'/refresh_token'); + +if($refresh_token){ + //若存在refresh token,则刷新它。 + $token_array=do_oauth_refresh(file_get_contents(CONFIG_DIR.'/appkey') , file_get_contents(CONFIG_DIR.'/appsec') , file_get_contents(CONFIG_DIR.'/refresh_token')); + if($token_array['access_token'] && $token_array['refresh_token']){ + //防止获取不到token而自杀的行为 + $access_token = $token_array['access_token']; + $refresh_token = $token_array['refresh_token']; + file_put_contents(CONFIG_DIR.'/access_token',$access_token); + file_put_contents(CONFIG_DIR.'/refresh_token',$refresh_token); + } +} + +switch($argv[1]){ +case 'quota': + //quota - 获取空间 + $quota = get_quota($access_token); + $u=$quota['used']/1024/1024/1024;$a=$quota['quota']/1024/1024/1024; + echon(sprintf("Your Storage Status: %.2fG/%.2fG (%.2f%%)",$u,$a,$u/$a*100)); + break; +case 'upload': + //upload - 上传文件 + if(count($argv)<3){ + echon("Missing parameters. Please check again."); + die(); + } + $res=upload_file($access_token,$argv[3],$argv[2]); + echon(sprintf("File %s uploaded.\nSize:%.3fK MD5 Sum:%s",$res['path'],$res['size']/1024,$res['md5'])); + break; +case 'download': + //download - 下载文件 + if(count($argv)<3){ + echon("Missing parameters. Please check again."); + die(); + } + $path='/apps/'.urlencode(file_get_contents(CONFIG_DIR.'/appname').'/'.$argv[3]); + $cmd = 'wget --no-check-certificate -O "'.$argv[2].' " "https://d.pcs.baidu.com/rest/2.0/pcs/file?method=download&access_token='.$access_token.'&path='.$path.'"'; + cmd($cmd); + break; +case 'delete': + //delete - 删除文件 + if(count($argv)<2){ + echon("Missing parameters. Please check again."); + die(); + } + delete_file($access_token,$argv[2]); + echon('File deleted.'); + break; +case 'fetch': + //fetch - 离线下载 + //好像需要一定的权限,无法使用。 + if(count($argv)<3){ + echon("Missing parameters. Please check again."); + die(); + } + fetch_file($access_token,$argv[2],$argv[3]); + break; +case 'uploadbig': + //uploadbig - 大文件上传 + switch(count($argv)){ + case 0: + case 1: + case 2: + case 3: //参数数目不够 + echon('Missing parameters. Please check again.'); + die(9099); + case 4: //设置默认值(单个文件大小->1G) + $argv[4] = 1073741824; + //因为需要继续下面的操作所以这里没有break + case 5: //设置默认值(临时文件目录->/tmp/) + $argv[5] = '/tmp/'; + //因为需要继续下面的操作所以这里没有break + default: //开始上传操作 + super_file($access_token,$argv[3],$argv[2],'newcopy',$argv[4],$argv[5]); + } +}