feat: 新增实现文件夹下载以及多文件压缩下载功能

This commit is contained in:
ganzizi 2023-11-10 19:34:36 +08:00
parent e7239b3c5c
commit 930f55b080
4 changed files with 177 additions and 3 deletions

View File

@ -868,8 +868,7 @@ class DialogController extends AbstractController
}
/**
* @api {post} api/dialog/msg/sendfiles 18. 群发文件上传
* @api {post} api/dialog/msg/sendfiles 18. 群发文件上传
* @api {post} api/dialog/msg/sendfiles 19. 群发文件上传
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0

View File

@ -14,6 +14,7 @@ use App\Module\Ihttp;
use Carbon\Carbon;
use Redirect;
use Request;
use ZipArchive;
/**
* @apiDefine file
@ -969,4 +970,55 @@ class FileController extends AbstractController
//
return Base::retSuccess('success', $fileLink);
}
/**
* @api {get} api/file/download/zip 19. 压缩下载
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup file
* @apiName download__zip
*
* @apiParam {Array} [ids] 文件ID格式: [id, id2, id3]
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function download__zip()
{
$user = User::auth();
$ids = Request::input('ids');
if (!is_array($ids) || empty($ids)) {
return Base::retError('Please select the downloaded file or folder');
}
if (count($ids) > 100) {
return Base::retError('You can only download up to 100 files or folders at a time');
}
$files = [];
AbstractModel::transaction(function() use ($user, $ids, &$files) {
foreach ($ids as $id) {
$files[] = File::getFilesTree(intval($id), $user, 0);
}
});
$zip = new \ZipArchive();
$zipName = 'temp/download/' . date("Ym") . '/' . $user->userid . '/file_' . date("YmdHis") . '.zip';
$zipPath = storage_path('app/'.$zipName);
Base::makeDir(dirname($zipPath));
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
return Base::retError('创建压缩文件失败');
}
array_walk($files, function($file) use ($zip) {
File::addFileTreeToZip($zip, $file);
});
$zip->close();
return response()->download($zipPath, 'file_'. date("YmdHis") .'.zip')->deleteFileAfterSend(true);
}
}

View File

@ -317,7 +317,7 @@ class File extends AbstractModel
$data['full_name'] = $webkitRelativePath ?: $data['name'];
//
$addItem[] = $data;
return ['data'=>$data,'addItem'=>$addItem];
});
}
@ -832,4 +832,98 @@ class File extends AbstractModel
}
});
}
/**
* 获取文件树
*
* @param int $fileId
* @param User $user
* @param int $permission
* @param string $path
* @return object
*/
public static function getFilesTree(int $fileId, User $user, $permission = 1, $path = '') {
$file = File::permissionFind($fileId, $user, $permission);
$file->path = ltrim($path . '/' . $file->name, '/');
$file->children = [];
if ($file->type == 'folder') {
$files = $file->getFileList($user, $fileId, 'all', false);
foreach ($files as &$childFile) {
$childFile['path'] = $file->path . '/' . $childFile['name'];
if ($childFile['type'] == 'folder') {
$childFile['children'] = self::getFilesTree($childFile['id'], $user, $permission, $file->path);
}
}
$file->children = $files;
}
$totalSize = self::calculateTotalSize($file);
if ($totalSize > 1024 * 1024 * 1024) { // 1GB
throw new ApiException('The total file size has exceeded 1GB, please download in batches');
}
return $file;
}
/**
* 计算文件夹文件总大小
*
* @param [type] $fileTree
* @return float|int
*/
public static function calculateTotalSize($fileTree) {
$totalSize = 0;
if ($fileTree->type != 'folder') {
$totalSize += $fileTree->size;
} else {
foreach ($fileTree->children as $childFile) {
$totalSize += self::calculateTotalSize((object)$childFile);
}
}
return $totalSize;
}
/**
* 文件夹文件添加到压缩文件
*
* @param [type] $zip
* @param object $file
* @return void
*/
public static function addFileTreeToZip($zip, $file)
{
if ($file->type != 'folder') {
$content = FileContent::whereFid($file->id)->orderByDesc('id')->first();
$content = Base::json2array($content?->content ?: []);
$typeExtensions = [
'word' => 'docx',
'excel' => 'xlsx',
'ppt' => 'pptx',
];
if (array_key_exists($file->type, $typeExtensions)) {
$filePath = empty($content) ? public_path('assets/office/empty.' . $typeExtensions[$file->type]) : public_path($content['url']);
}
$relativePath = $file->path . '.' . $file->ext;
if (file_exists($filePath)) {
$zip->addFile($filePath, $relativePath);
} else {
if (empty($content['url'])) {
$zip->addFromString($relativePath, $content['content']);
} else {
$filePath = public_path($content['url']);
$zip->addFile($filePath, $relativePath);
}
}
} else {
if (isset($file->children)) {
foreach ($file->children as $childFile) {
try {
self::addFileTreeToZip($zip, (object)$childFile);
} catch (\Exception $e) {
}
}
}
// 在压缩包中创建文件夹
$zip->addEmptyDir($file->path);
}
}
}

View File

@ -49,6 +49,10 @@
<Icon type="ios-cut" />
{{$L('剪切')}}
</Button>
<Button size="small" type="info" @click="downloadZipFile(selectIds)">
<Icon type="ios-download-outline" />
{{$L('压缩下载')}}
</Button>
<Button size="small" type="error" @click="deleteFile(selectIds)">
<Icon type="ios-trash" />
{{$L('删除')}}
@ -195,6 +199,7 @@
<DropdownItem name="send" :disabled="contextMenuItem.type == 'folder'">{{$L('发送')}}</DropdownItem>
<DropdownItem name="link" :divided="contextMenuItem.userid != userId && !contextMenuItem.share" :disabled="contextMenuItem.type == 'folder'">{{$L('链接')}}</DropdownItem>
<DropdownItem name="download" :disabled="contextMenuItem.ext == ''">{{$L('下载')}}</DropdownItem>
<DropdownItem name="downloadzip">{{$L('压缩下载')}}</DropdownItem>
<DropdownItem name="delete" divided style="color:red">{{$L('删除')}}</DropdownItem>
</template>
@ -1260,6 +1265,10 @@ export default {
});
break;
case 'downloadzip':
this.downloadZipFile([item.id])
break;
case 'delete':
this.deleteFile([item.id])
break;
@ -1396,6 +1405,26 @@ export default {
});
},
downloadZipFile(ids){
if (ids.length === 0) {
return
}
const firstFile = this.fileLists.find(({ id }) => id === ids[0]) || {};
const allFolder = !ids.some(id => this.fileLists.some(({ type, id: itemId }) => type !== 'folder' && itemId === id));
const typeName = allFolder ? "文件夹" : "文件";
const fileName = ids.length === 1 ? `${firstFile.name}${typeName}` : `${firstFile.name}】等${ids.length}${typeName}`;
$A.modalConfirm({
title: '下载文件',
content: `你确定要下载${fileName}吗?`,
okText: '立即下载',
onOk: () => {
const idsParam = ids.join('&ids[]=');
this.$store.dispatch('downUrl', $A.apiUrl(`file/download/zip?ids[]=${idsParam}`));
}
});
},
autoBlur(id) {
this.$nextTick(() => {
if (this.$refs['input_' + id]) {