complete remote download based on aria2

pull/64/head
HFO4 8 years ago
parent 334d25833c
commit 36e6526259

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

@ -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.'));
}
@ -483,4 +487,31 @@ class Admin extends Controller{
]);
}
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'=>"取消失败"]);
}
}
}

@ -129,7 +129,7 @@ class RemoteDownload extends Controller{
}
public function ListDownloading(){
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","in",["active","ready"])->order('id desc')->select();
$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"])){
@ -153,7 +153,7 @@ class RemoteDownload extends Controller{
public function ListFinished(){
$page = input("get.page");
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","not in",["active","ready"])->order('id desc')->page($page.',10')->select();
$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"])){

@ -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');

@ -42,7 +42,7 @@ class Aria2 extends Model{
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = $respondData["error"]["message"];
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
@ -70,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)){
@ -148,15 +153,15 @@ class Aria2 extends Model{
}else{
$this->reqStatus = 0;
$this->reqMsg = $respondData["error"]["message"];
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",false);
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",true);
return false;
}
return true;
}
private function setCanceled($quenInfo,$sqlData){
@self::remove_directory(ROOT_PATH."public".DS."downloads".DS.$sqlData["path_id"]);
if(!is_dir(ROOT_PATH."public".DS."downloads".DS.$sqlData["path_id"])){
@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",
]);

@ -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");
}
}
?>

@ -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>

@ -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}

@ -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}

@ -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>

@ -57,7 +57,6 @@ padding-left: 15px;
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="completeItemContent">

@ -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

@ -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();
}
})

@ -48,7 +48,6 @@ $(function() {
row = row +'<td class="centerTable"><span class="download-success">完成</span></td>'
break;
}
row = row + '<td class="centerTable"><a href="javascript:" onclick="delete('+e["id"]+')" >删除记录</a></td>'
return row + "</tr>";
});
switch(e["status"]){
@ -101,7 +100,7 @@ function loadDownloadingList() {
function bytesToSize(bytes) {
if (bytes === 0) return '0 B';
var k = 1000, // or 1024
var k = 1024, // or 1024
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
@ -119,7 +118,6 @@ function loadDownloadingList() {
function cancel(id){
$.post("/RemoteDownload/Cancel", {id:id}, function(data){
console.log(data);
if(data.error){
toastr["warning"](data.message);
}else{

Loading…
Cancel
Save