Merge pull request #64 from HFO4/dev

remote download complete
This commit is contained in:
AaronLiu 2018-04-19 19:09:22 +08:00 committed by GitHub
commit 9651caa219
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 979 additions and 59 deletions

View file

@ -16,6 +16,7 @@ Cloudreve - Make the cloud easy for everyone
* 快速对接多家云存储支持七牛、又拍云、阿里云OSS、AWS S3、自建远程服务器当然还有本地存储
* 可限制单文件最大大小、MIMEType、文件后缀、用户可用容量
* 基于Aria2的离线下载
* 图片、音频、视频、文本、Markdown、Ofiice文档 在线预览
* 移动端全站响应式布局
* 文件、目录分享系统,可创建私有分享或公开分享链接

View file

@ -248,6 +248,10 @@ class Admin extends Controller{
return $this->adminObj->saveMailSetting(input('post.'));
}
public function SaveAria2Setting(){
return $this->adminObj->saveAria2Setting(input('post.'));
}
public function SendTestMail(){
return $this->adminObj->sendTestMail(input('post.'));
}
@ -482,5 +486,32 @@ class Admin extends Controller{
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
public function RemoteDownload(){
$this->adminObj->listDownloads();
return view('download', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["aria2"]),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function CancelDownload(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new \app\index\model\Aria2($aria2Options);
$downloadItem = Db::name("download")->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
}

View file

@ -38,6 +38,16 @@ class Home extends Controller{
]);
}
public function Download(){
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
return view('download', [
'options' => Option::getValues(['basic','group_sell']),
'userInfo' => $userInfo,
'groupData' => $groupData,
]);
}
public function Album(){
$userInfo = $this->userObj->getInfo();
$list = Db::name("files")->where("upload_user",$this->userObj->uid)
@ -60,7 +70,7 @@ class Home extends Controller{
$listData = $list->all();
$pageNow = input("?get.page")?input("get.page"):1;
if($pageNow>$pageCount){
$this->error('页面不存在',404,Option::getValues(['basic','group_sell']));
$this->error('您当前没有上传任何图片',404,Option::getValues(['basic','group_sell']));
}
return view('album', [
'options' => Option::getValues(['basic','group_sell']),

View file

@ -29,16 +29,19 @@ class RemoteDownload extends Controller{
return true;
}
private function insertRecord($aria2,$url){
private function insertRecord($aria2,$url,$path){
Db::name("download")->insert([
"pid" => $aria2->pid,
"path_id" => $aria2->pathId,
"owner" => $this->userObj->uid,
"save_dir" => 1,
"save_dir" => $path,
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$url,
"file_index" => 0,
"is_single" => 1,
"total_size" => 0,
]);
}
@ -51,9 +54,36 @@ class RemoteDownload extends Controller{
$aria2 = new Aria2($aria2Options);
$downloadStart = $aria2->addUrl(input("post.url"));
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.url"));
$this->insertRecord($aria2,input("post.url"),input("post.path"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(['error'=>1,'message'=>$aria2->reqMsg]);
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
public function AddTorrent(){
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$this->checkPerimission(0) || $policyData["policy_type"] != "local"){
return json(['error'=>1,'message'=>'您当前的无用户无法执行此操作']);
}
$downloadingLength = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->sum("total_size");
if(!\app\index\model\FileManage::sotrageCheck($this->userObj->uid,$downloadingLength)){
return json(["result"=>['success'=>false,'error'=>"容量不足"]]);
}
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$torrentObj = new \app\index\model\FileManage(input("post.id"),$this->userObj->uid,true);
$downloadStart = $aria2->addTorrent($torrentObj->signTmpUrl());
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.id"),input("post.savePath"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
@ -69,4 +99,81 @@ class RemoteDownload extends Controller{
}
}
public function FlushUser(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
//取消的
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$this->userObj->uid,$this->userObj->getPolicy());
}
}
public function Cancel(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$downloadItem = Db::name("download")->where("owner",$this->userObj->uid)->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
public function ListDownloading(){
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","in",["active","ready","waiting"])->order('id desc')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
public function ListFinished(){
$page = input("get.page");
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","not in",["active","ready","waiting"])->order('id desc')->page($page.',10')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
}

View file

@ -104,6 +104,10 @@ class AdminHandler extends Model{
return $this->saveOptions($options);
}
public function saveAria2Setting($options){
return $this->saveOptions($options);
}
public function saveMailTemplate($options){
return $this->saveOptions($options);
}
@ -113,6 +117,7 @@ class AdminHandler extends Model{
unset($options["sizeTimes"]);
$options["grade_policy"] = 0;
$options["policy_list"] = $options["policy_name"];
$options["aria2"] = $options["aria2"] ? "1,1,1" : "0,0,0";
try {
Db::name("groups")->insert($options);
} catch (Exception $e) {
@ -347,6 +352,46 @@ class AdminHandler extends Model{
return $userData;
}
public function listDownloads(){
$pageSize = 10;
$this->pageData = Db::name("download")
->order("id desc")
->paginate($pageSize);
$this->dataTotal = Db::name("download")
->order("id desc")
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$userCache=[];
$userCacheList=[];
foreach ($this->listData as $key => $value) {
if(in_array($value["owner"], $userCacheList)){
$this->listData[$key]["user"] = $userCache[$value["owner"]];
}else{
$this->listData[$key]["user"] = Db::name("users")->where("id",$value["owner"])->find();
array_push($userCacheList,$value["owner"]);
$userCache[$value["owner"]] = $this->listData[$key]["user"];
}
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$this->listData[$key]["fileName"] = basename($connectInfo["dir"]);
$this->listData[$key]["completedLength"] = $connectInfo["completedLength"];
$this->listData[$key]["totalLength"] = $connectInfo["totalLength"];
$this->listData[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$this->listData[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name")[0];
}else{
$this->listData[$key]["fileName"] = $value["source"];
}
$this->listData[$key]["completedLength"] = 0;
$this->listData[$key]["totalLength"] = 0;
$this->listData[$key]["downloadSpeed"] = 0;
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function listFile(){
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodFile')) ? "id DESC" : cookie('orderMethodFile');

View file

@ -37,6 +37,28 @@ class Aria2 extends Model{
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
public function addTorrent($torrentUrl){
$this->pathId = uniqid();
$reqFileds = [
"params" => ["token:".$this->authToken,
[$torrentUrl],["dir" => $this->savePath.$this->pathId],
],
"jsonrpc" => "2.0",
"id" => $this->pathId,
"method" => "aria2.addUri"
];
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
@ -48,6 +70,11 @@ class Aria2 extends Model{
public function flushStatus($id,$uid,$policy){
$this->uid = $uid;
if(empty($policy)){
$user = Db::name("users")->where("id",$uid)->find();
$group = Db::name("groups")->where("id",$user["user_group"])->find();
$policy = Db::name("policy")->where("id",$group["policy_name"])->find();
}
$this->policy = $policy;
$downloadInfo = Db::name("download")->where("id",$id)->find();
if(empty($downloadInfo)){
@ -74,45 +101,114 @@ class Aria2 extends Model{
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
if($this->storageCheck($respondData["result"],$downloadInfo)){
if($downloadInfo["is_single"] && count($respondData["result"]["files"]) >1){
$this->updateToMuiltpe($respondData["result"],$downloadInfo);
return false;
}
if(isset($respondData["result"]["followedBy"])){
Db::name("download")->where("id",$id)
->update([
"pid" => $respondData["result"]["followedBy"][0],
]);
return false;
}
Db::name("download")->where("id",$id)
->update([
"status" => $respondData["result"]["status"],
"last_update" => date("Y-m-d h:i:s"),
"info" => json_encode([
"completedLength" => $respondData["result"]["completedLength"],
"totalLength" => $respondData["result"]["totalLength"],
"dir" => $respondData["result"]["files"][0]["path"],
"completedLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"],
"totalLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
"dir" => $respondData["result"]["files"][$downloadInfo["file_index"]]["path"],
"downloadSpeed" => $respondData["result"]["downloadSpeed"],
"errorMessage" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
]),
"msg" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
"total_size" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
]);
switch ($respondData["result"]["status"]) {
case 'complete':
$this->setComplete($respondData["result"],$downloadInfo);
break;
case 'removed':
$this->setCanceled($respondData["result"],$downloadInfo);
break;
default:
# code...
break;
}
if(($respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"] == $respondData["result"]["files"][$downloadInfo["file_index"]]["length"]) && $respondData["result"]["status"]=="active"){
$this->setComplete($respondData["result"],$downloadInfo);
Db::name("download")->where("id",$id)
->update([
"status" => "complete",
]);
}
}else{
$this->reqStatus = 0;
$this->reqMsg = "空间容量不足";
//取消离线下载
$this->setError($respondData["result"],$downloadInfo,"空间容量不足");
return false;
}
}else{
$this->reqStatus = 0;
$this->reqMsg = $respondData["error"]["message"];
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",true);
return false;
}
return true;
}
private function setCanceled($quenInfo,$sqlData){
@self::remove_directory($this->savePath.$sqlData["path_id"]);
if(!is_dir($this->savePath.$sqlData["path_id"])){
Db::name("download")->where("id",$sqlData["id"])->update([
"status" => "canceled",
]);
}
}
static function remove_directory($dir){
if($handle=opendir("$dir")){
while(false!==($item=readdir($handle))){
if($item!="."&&$item!=".."){
if(is_dir("$dir/$item")){
self::remove_directory("$dir/$item");
}else{
unlink("$dir/$item");
}
}
}
closedir($handle);
rmdir($dir);
}
}
private function updateToMuiltpe($quenInfo,$sqlData){
foreach ($quenInfo["files"] as $key => $value) {
Db::name("download")->insert([
"pid" => $sqlData["pid"],
"path_id" => $sqlData["path_id"],
"owner" => $sqlData["owner"],
"save_dir" => $sqlData["save_dir"],
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$sqlData["source"],
"file_index" => $key,
"is_single" => 0,
"total_size" => 0,
]);
}
Db::name("download")->where("id",$sqlData["id"])->delete();
}
private function setComplete($quenInfo,$sqlData){
if($this->policy["policy_type"] != "local"){
//取消任务
$this->setError($quenInfo,$sqlData,"您当前的上传策略无法使用离线下载");
return false;
}
$this->forceRemove($sqlData["pid"]);
$suffixTmp = explode('.', $quenInfo["dir"]);
$fileSuffix = array_pop($suffixTmp);
$uploadHandller = new UploadHandler($this->policy["id"],$this->uid);
@ -123,45 +219,101 @@ class Aria2 extends Model{
}
if($sufficCheck){
//取消任务
$this->setError();
$this->setError($quenInfo,$sqlData,"文件类型不被允许");
return false;
}
if($this->policy['autoname']){
$fileName = $uploadHandller->getObjName($this->policy['namerule'],"local",basename($quenInfo["files"][0]["path"]));
$fileName = $uploadHandller->getObjName($this->policy['namerule'],"local",basename($quenInfo["files"][$sqlData["file_index"]]["path"]));
}else{
$fileName = basename($quenInfo["files"][0]["path"]);
$fileName = basename($quenInfo["files"][$sqlData["file_index"]]["path"]);
}
$generatePath = $uploadHandller->getDirName($this->policy['dirrule']);
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
is_dir(dirname($savePath))? :mkdir(dirname($savePath),0777,true);
rename($quenInfo["files"][0]["path"],$savePath);
@unlink(dirname($quenInfo["files"][0]["path"]));
rename($quenInfo["files"][$sqlData["file_index"]]["path"],$savePath);
@unlink(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
$jsonData = array(
"path" => "",
"fname" => basename($quenInfo["files"][0]["path"]),
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
"objname" => $generatePath.DS.$fileName,
"fsize" => $quenInfo["totalLength"],
"fsize" => $quenInfo["files"][$sqlData["file_index"]]["length"],
);
@list($width, $height, $type, $attr) = getimagesize($savePath);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$this->policy,$this->uid,$picInfo);
if(!$addAction[0]){
//取消任务
$this->setError();
$this->setError($quenInfo,$sqlData,$addAction[1]);
return false;
}
FileManage::storageCheckOut($this->uid,(int)$quenInfo["totalLength"]);
FileManage::storageCheckOut($this->uid,$quenInfo["files"][$sqlData["file_index"]]["length"]);
}
private function setError(){
private function setError($quenInfo,$sqlData,$msg,$status="error",$delete=true){
$this->Remove($sqlData["pid"],$sqlData);
$this->removeDownloadResult($sqlData["pid"],$sqlData);
if($delete){
if(file_exists($quenInfo["files"][$sqlData["file_index"]]["path"])){
@unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
}
}
Db::name("download")->where("id",$sqlData["id"])->update([
"msg" => $msg,
"status" => $status,
]);
}
public function Remove($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.remove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
public function removeDownloadResult($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.removeDownloadResult"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
public function forceRemove($gid){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.forceRemove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
private function storageCheck($quenInfo,$sqlData){
if(!FileManage::sotrageCheck($this->uid,(int)$quenInfo["totalLength"])){
if(!FileManage::sotrageCheck($this->uid,$quenInfo["totalLength"])){
return false;
}
if(!FileManage::sotrageCheck($this->uid,(int)$quenInfo["completedLength"])){
if(!FileManage::sotrageCheck($this->uid,$quenInfo["completedLength"])){
return false;
}
return true;
@ -169,18 +321,18 @@ class Aria2 extends Model{
private function sendReq($data){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $this->apiUrl."jsonrpc");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$tmpInfo = curl_exec($curl);
if (curl_errno($curl)) {
$this->reqStatus = 0;
$this->reqMsg = "请求失败,".curl_error($curl);
}
curl_close($curl);
return json_decode($tmpInfo,true);
curl_setopt($curl, CURLOPT_URL, $this->apiUrl."jsonrpc");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$tmpInfo = curl_exec($curl);
if (curl_errno($curl)) {
$this->reqStatus = 0;
$this->reqMsg = "请求失败,".curl_error($curl);
}
curl_close($curl);
return json_decode($tmpInfo,true);
}
}

View file

@ -7,6 +7,7 @@ use \think\Session;
use \app\index\model\FileManage;
use \app\index\model\Option;
use \app\index\model\Mail;
use \app\index\model\Aria2;
class CronHandler extends Model{
@ -40,6 +41,11 @@ class CronHandler extends Model{
$this->deleteCallbackData($value["interval_s"]);
}
break;
case 'flush_aria2':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->flushAria2($value["interval_s"]);
}
break;
default:
# code...
break;
@ -69,5 +75,21 @@ class CronHandler extends Model{
$this->setComplete("delete_callback_data");
}
public function flushAria2($interval){
echo("flushingAria2Status...");
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$value["owner"],null);
}
echo("Complete<br>");
$this->setComplete("flush_aria2");
}
}
?>

View file

@ -47,7 +47,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
$fileSize = fstat($data)["size"];
if(empty($fileSize)){
$fileSize = 0;
$fileSize = -1;
}
if($fileSize>$policyData["max_size"]){
throw new DAV\Exception\InsufficientStorage('File is to large');
@ -66,6 +66,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
mkdir($savePath,0777,true);
}
file_put_contents($savePath."/".$fileName, $data);
if($fileSize<=0){
$fileSize = filesize($savePath."/".$fileName);
}
$jsonData = array(
"path" => str_replace("/",",",ltrim($this->myPath,"/")),
"fname" => $name,

View file

@ -22,12 +22,17 @@ class FileManage extends Model{
public $policyData;
public $deleteStatus = true;
public function __construct($path,$uid){
$this->filePath = $path;
$fileInfo = $this->getFileName($path);
$fileName = $fileInfo[0];
$path = $fileInfo[1];
$fileRecord = Db::name('files')->where('upload_user',$uid)->where('orign_name',$fileName)->where('dir',$path)->find();
public function __construct($path,$uid,$byId=false){
if($byId){
$fileRecord = Db::name('files')->where('id',$path)->find();
$this->filePath = rtrim($fileRecord["dir"],"/")."/".$fileRecord["orign_name"];
}else{
$this->filePath = $path;
$fileInfo = $this->getFileName($path);
$fileName = $fileInfo[0];
$path = $fileInfo[1];
$fileRecord = Db::name('files')->where('upload_user',$uid)->where('orign_name',$fileName)->where('dir',$path)->find();
}
if (empty($fileRecord)){
die('{ "result": { "success": false, "error": "文件不存在" } }');
}

View file

@ -113,6 +113,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0" checked>
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>

View file

@ -0,0 +1,122 @@
{extend name="header_admin" /}
{block name="title"}离线下载 - {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">离线下载</li>
<li class="breadcrumb-item active">配置</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>离线下载</h2>
<br>
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#options"><i class="fa fa-cog" aria-hidden="true"></i> Aria2配置</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list"><i class="fa fa-list" aria-hidden="true"></i> 任务列表</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" >
<div class="tab-pane fade show active" id="options" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="aria2Options">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Server</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_rpcurl" value="{$optionsForSet.aria2_rpcurl}" spellcheck="false"></div>
<div class="col-md-4 option-des"> aria2的RPC服务器地址请在aria2的配置文件中启用RPC服务。例如http://127.0.0.1:6800/</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Token</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_token" value="{$optionsForSet.aria2_token}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 在配置文件中设置的RPC服务的Token</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">下载临时目录</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_tmppath" value="{$optionsForSet.aria2_tmppath}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 下载文件数据的临时存放目录请确保PHP对该目录拥有读写权限</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="aria2_options" class="col-form-label col-form-label-sm">其他下载参数</label>
</div>
<div class="col-md-4"> <textarea class="form-control" name="aria2_options" spellcheck="false">{$optionsForSet.aria2_options}</textarea></div>
<div class="col-md-4 option-des"> aria2启动下载的其他附带参数请以json格式书写。你可也可以将这些设置写在aria2配置文件里。可用参数请查阅<a href="https://aria2.github.io/manual/en/html/aria2c.html#options" target="_blank">官方文档</a></div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-primary" id="saveAria2">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="list" role="tabpanel" aria-labelledby="pills-profile-tab">
<table class="table">
<thead>
<tr>
<th scope="col" class="textCenter">#</th>
<th scope="col" width="50%">文件名</th>
<th scope="col" class="textCenter">创建者</th>
<th scope="col" class="textCenter">文件大小</th>
<th scope="col" class="textCenter">状态</th>
<th scope="col" class="textCenter">进度</th>
<th scope="col" class="textCenter">操作</th>
</tr>
</thead>
<tbody id="listContent">
{volist name='list' id='download'}
<tr id="i-{$download.id}" data-pid="{$download.pid}">
<th scope="row" class="textCenter">{$download.id}</th>
<td>{:$originList[$key]['fileName']}</td>
<td class="textCenter">{:$originList[$key]['user']["user_nick"]}</td>
<td class="textCenter">{:countSize($download.total_size)}</td>
<td class="textCenter">{$download.status}</td>
<td class="textCenter">{:floor($originList[$key]['completedLength']/$originList[$key]['totalLength']*10000)/100}%</td>
<td class="textCenter">{eq name="download.status" value="active"}<a href="javascript:" onclick="cancel('{$download.id}')">取消任务</a>{else/} - {/eq}</td>
</tr>
{/volist}
</tbody>
</table>
{$list->render()}
</div>
<div class="tab-pane fade" id="tools" role="tabpanel" aria-labelledby="pills-profile-tab">
ddd
</div>
</div>
</div>
</div><br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/summernote.min.js"></script>
<script src="/static/js/admin/summernote-zh-CN.min.js"></script>
<script src="/static/js/admin/aria2.js"></script>
{/block}

View file

@ -114,6 +114,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0">
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>
@ -167,5 +179,10 @@ $("input[name='range_transfer'][value='{$group.range_transfer}']").attr("checked
$("input[name='allow_share'][value='{$group.allow_share}']").attr("checked",true);
$("input[name='color'][value='{$group.color}']").attr("checked",true);
$("input[name='webdav'][value='{$group.webdav}']").attr("checked",true);
if('{$group.aria2}'=="1,1,1"){
$("input[name='aria2'][value='1']").attr("checked",true);
}else{
$("input[name='aria2'][value='0']").attr("checked",true);
}
</script>
{/block}

View file

@ -76,6 +76,12 @@
<span class="nav-link-text">分享</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="离线下载">
<a class="nav-link" href="/Admin/RemoteDownload">
<i class="fa fa-fw fa-cloud-download"></i>
<span class="nav-link-text">离线下载</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="用户">
<a class="nav-link" href="/Admin/Users" data-parent="#user">
<i class="fa fa-fw fa-user"></i>

View file

@ -0,0 +1,82 @@
{extend name="header_home" /}
{block name="title"}离线下载管理- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/remoteDownload.js"></script>
<style type="text/css">
.col-md-3{
padding-right: 15px;
padding-left: 15px;
}
</style>
</head>
<body >
<div id="container">
{include file="navbar_home" /}
<div class="col-md-10 quota_content">
<h1>离线下载管理</h1>
<br>
<div class="fix_side">
<div class="fix">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">正在进行中 <i class="fa fa-refresh" aria-hidden="true" title="刷新数据" onclick="refresh()"></i></div>
<div class="panel-body centerTable" id="loadStatus">
更新状态数据中...
</div>
<div class="table-responsive" style="display: none">
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">下载速度</th>
<th class="centerTable">进度</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="itemContent">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">已完成 </i></div>
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
</tr>
</thead>
<tbody id="completeItemContent">
</tbody>
</table>
</div>
<div class="panel-body centerTable" id="loadButton">
<button class="btn btn-primary" id="loadFinished">点击加载已完成列表</button>
</div>
</div>
</div>
</div>
<br>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}

View file

@ -28,6 +28,8 @@
allowSource : "{$policyData.origin_link}",
upUrl : "{$policyData.server}",
allowShare:"{$groupData.allow_share}",
allowRemoteDownload:"{:explode(",",$groupData.aria2)[0]}",
allowTorrentDownload:"{:explode(",",$groupData.aria2)[1]}",
};
</script>
<script src="/static/js/home.js"></script>

View file

@ -129,6 +129,11 @@
<a href="/Home/Album" class="list-group-item">
<i class="fa fa-picture-o" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 图片集
</a>
{eq name=':(explode(",",$groupData.aria2)[0] OR explode(",",$groupData.aria2)[1])' value="1"}
<a href="/Home/Download" class="list-group-item">
<i class="fa fa-cloud-download" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 离线下载
</a>
{/eq}
</div>
<div clss="usage" style=" visibility: visible;
position: absolute;

View file

@ -71,7 +71,8 @@ CREATE TABLE `sd_corn` (
INSERT INTO `sd_corn` (`id`, `rank`, `name`, `des`, `last_excute`, `interval_s`, `enable`) VALUES
(1, 2, 'delete_unseful_chunks', '删除分片上传产生的失效文件块', 0, 3600, 1),
(5, 1, 'delete_callback_data', '删除callback记录', 0, 86400, 1);
(5, 1, 'delete_callback_data', '删除callback记录', 0, 86400, 1),
(NULL, 1, 'flush_aria2', '刷新离线下载状态', 0, 30, 1);
-- --------------------------------------------------------
@ -132,17 +133,18 @@ CREATE TABLE `sd_groups` (
`color` text NOT NULL,
`policy_list` text NOT NULL,
`range_transfer` tinyint(1) NOT NULL,
`webdav` tinyint(1) NOT NULL
`webdav` tinyint(1) NOT NULL,
`aria2` text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- 转存表中的数据 `sd_groups`
--
INSERT INTO `sd_groups` (`id`, `group_name`, `policy_name`, `max_storage`, `grade_policy`, `speed`, `allow_share`, `color`, `policy_list`, `range_transfer`, `webdav`) VALUES
(1, '管理员', 1, 1073741824, '', '', 1, 'danger', '1', 1, 1),
(2, '游客', 1, 0, '', '', 1, 'default', '1', 0, 0),
(3, '注册会员', 1, 52428800, '', '', 1, 'default', '1', 1, 1);
INSERT INTO `sd_groups` (`id`, `group_name`, `policy_name`, `max_storage`, `grade_policy`, `speed`, `allow_share`, `color`, `policy_list`, `range_transfer`, `webdav`,`aria2`) VALUES
(1, '管理员', 1, 1073741824, '', '', 1, 'danger', '1', 1, 1, "0,0,0"),
(2, '游客', 1, 0, '', '', 1, 'default', '1', 0, 0, "0,0,0"),
(3, '注册会员', 1, 52428800, '', '', 1, 'default', '1', 1, 1, "0,0,0");
-- --------------------------------------------------------
@ -194,7 +196,11 @@ INSERT INTO `sd_options` (`id`, `option_name`, `option_value`, `option_type`) VA
(46, 'admin_color_nav', 'navbar navbar-expand-lg fixed-top navbar-light bg-light', 'admin'),
(47, 'js_code', '<script type=\"text/javascript\">\r\n\r\n</script>', 'basic'),
(50, 'sendfile', '0', 'download'),
(51, 'header', 'X-Sendfile', 'download');
(51, 'header', 'X-Sendfile', 'download'),
(52, 'aria2_tmppath', '/path/to/public/download', 'aria2'),
(53, 'aria2_token', 'your token', 'aria2'),
(54, 'aria2_rpcurl', 'http://127.0.0.1:6800/', 'aria2'),
(55, 'aria2_options', '{\"max-tries\":5}', 'aria2');
-- --------------------------------------------------------
@ -295,7 +301,25 @@ CREATE TABLE `sd_users` (
INSERT INTO `sd_users` (`id`, `user_email`, `user_nick`, `user_pass`, `user_date`, `user_status`, `user_group`, `group_primary`, `user_activation_key`, `used_storage`, `two_step`, `delay_time`, `avatar`, `profile`, `webdav_key`) VALUES
(1, 'admin@cloudreve.org', 'Admin', 'd8446059f8846a2c111a7f53515665fb', '2018-01-30 02:13:34', 0, 1, 0, 'n', 0, '0', 0, 'default', 1, 'd8446059f8846a2c111a7f53515665fb');
CREATE TABLE `sd_download` (
`id` int(11) NOT NULL,
`pid` text NOT NULL,
`path_id` text NOT NULL,
`owner` int(11) NOT NULL,
`save_dir` text NOT NULL,
`status` text NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`msg` text NOT NULL,
`info` text NOT NULL,
`source` text NOT NULL,
`file_index` int(11) NOT NULL,
`is_single` tinyint(1) NOT NULL,
`total_size` bigint(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
ALTER TABLE `sd_download`
ADD PRIMARY KEY (`id`);
ALTER TABLE `sd_download`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- Indexes for dumped tables
--

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

34
static/js/admin/aria2.js Normal file
View file

@ -0,0 +1,34 @@
$("#saveAria2").click(function() {
$("#saveAria2").attr("disabled", "true");
$.post("/Admin/SaveAria2Setting",
$("#aria2Options").serialize()
, function(data) {
if (data.error == "1") {
toastr["warning"](data.msg);
$("#saveAria2").removeAttr("disabled");
} else if (data.error == "200") {
toastr["success"]("设置已保存");
$("#saveAria2").removeAttr("disabled");
}else{
toastr["warning"]("未知错误");
$("#saveAria2").removeAttr("disabled");
}
});
})
function cancel(id){
$.post("/Admin/CancelDownload", {id:id}, function(data){
if(data.error){
toastr["warning"](data.message);
}else{
var pid = $("#i-"+id).attr("data-pid");
$("[data-pid='"+pid+"'").remove();
toastr["success"](data.message);
}
})
}
$(document).ready(function(){
if(document.location.href.indexOf("page")!=-1){
$("[href='#list']").click();
}
})

File diff suppressed because one or more lines are too long

View file

@ -49,6 +49,9 @@
changePermissions: false,
upload: false,
shareFile: uploadConfig.allowShare == "1" ? true : false,
allowRemoteDownload: uploadConfig.allowRemoteDownload,
allowTorrentDownload: uploadConfig.allowTorrentDownload,
}),
});
}]);

137
static/js/remoteDownload.js Normal file
View file

@ -0,0 +1,137 @@
function getMemory() {
$.get("/Member/Memory", function(data) {
var dataObj = eval("(" + data + ")");
if (dataObj.rate >= 100) {
$("#memory_bar").css("width", "100%");
$("#memory_bar").addClass("progress-bar-warning");
toastr["error"]("您的已用容量已超过容量配额,请尽快删除多余文件或购买容量");
} else {
$("#memory_bar").css("width", dataObj.rate + "%");
}
$("#used").html(dataObj.used);
$("#total").html(dataObj.total);
});
}
page = 1;
window.onload = function() {
$.material.init();
getMemory();
}
$(function() {
$("#loadFinished").click(function(){
$.getJSON("/RemoteDownload/ListFinished?page="+page, function(data) {
if(data.length == 0){
$("#loadFinished").html("已加载全部");
$("#loadFinished").attr("disabled","true");
}else{
$("#loadFinished").html("继续加载");
}
data.forEach(function(e) {
$("#completeItemContent").append(function() {
var row = '<tr id="i-' + e["id"] + '" data-pid="'+e["pid"]+'"><th scope="row" class="centerTable">' + e["id"] + '</th><td>' + e["fileName"] + '</td>';
row = row + '<td class="centerTable">' + bytesToSize(e["totalLength"]) + '</td>';
row = row + '<td class="centerTable">' + e["save_dir"] + '</td>';
switch(e["status"]){
case "error":
row = row +'<td class="centerTable"><span class="download-error" data-toggle="tooltip" data-placement="top" title="'+e["msg"]+'">失败</span></td>'
break;
case "canceled":
row = row +'<td class="centerTable"><span >取消</span></td>'
break;
case "canceled":
row = row +'<td class="centerTable"><span >取消</span></td>'
break;
case "complete":
row = row +'<td class="centerTable"><span class="download-success">完成</span></td>'
break;
}
return row + "</tr>";
});
switch(e["status"]){
case "error":
$("#i-" + e["id"]).addClass("td-error");
$('[data-toggle="tooltip"]').tooltip()
break;
case "complete":
$("#i-" + e["id"]).addClass("td-success");
break;
}
});
page++;
})
})
})
$.get("/RemoteDownload/FlushUser", function() {
$("#loadStatus").html("加载下载列表中...");
loadDownloadingList();
})
function loadDownloadingList() {
$("#itemContent").html("");
$.getJSON("/RemoteDownload/ListDownloading", function(data) {
if(data.length == 0){
$("#loadStatus").html("下载列表为空");
}
data.forEach(function(e) {
$("#itemContent").append(function() {
var row = '<tr id="i-' + e["id"] + '" data-pid="'+e["pid"]+'"><th scope="row" class="centerTable">' + e["id"] + '</th><td>' + e["fileName"] + '</td>';
row = row + '<td class="centerTable">' + bytesToSize(e["totalLength"]) + '</td>';
row = row + '<td class="centerTable">' + e["save_dir"] + '</td>';
if (e["downloadSpeed"] == "0") {
row = row + '<td class="centerTable">-</td>';
} else {
row = row + '<td class="centerTable">' + bytesToSize(e["downloadSpeed"]) + '/s</td>';
}
row = row + '<td class="centerTable">' + GetPercent(e["completedLength"], e["totalLength"]) + '</td>'
row = row + '<td class="centerTable"><a href="javascript:" onclick="cancel('+e["id"]+')" >取消</a></td>'
return row + "</tr>";
});
$("#i-" + e["id"]).css({
"background-image": "-webkit-gradient(linear, left top, right top, from(#ecefff), to(white), color-stop("+e["completedLength"]/e["totalLength"]+", #ecefff), color-stop("+e["completedLength"]/e["totalLength"]+", white))",
});
$(".table-responsive").slideDown();
$("#loadStatus").slideUp();
});
})
}
function bytesToSize(bytes) {
if (bytes === 0) return '0 B';
var k = 1024, // or 1024
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
function GetPercent(num, total) {
num = parseFloat(num);
total = parseFloat(total);
if (isNaN(num) || isNaN(total)) {
return "-";
}
return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00 + "%");
}
function cancel(id){
$.post("/RemoteDownload/Cancel", {id:id}, function(data){
if(data.error){
toastr["warning"](data.message);
}else{
var pid = $("#i-"+id).attr("data-pid");
$("[data-pid='"+pid+"'").remove();
toastr["success"](data.message);
}
})
}
function refresh(){
$.get("/RemoteDownload/FlushUser?i="+Math.random(), function() {
$("#loadStatus").html("加载下载列表中...");
loadDownloadingList();
})
}