init: 初始系统模板

This commit is contained in:
TsMask
2023-09-05 14:38:23 +08:00
parent a5bc16ae4f
commit 1075c8ae4f
130 changed files with 22531 additions and 1 deletions

195
src/api/tool/file.ts Normal file
View File

@@ -0,0 +1,195 @@
import { request } from '@/plugins/http-fetch';
import { encode } from 'js-base64';
/**
* 下载文件
* @param filePath 文件路径带/,如:/upload/default/2023/06/xx.png
* @param range 断点续传标识,填入字符串 `bytes=${startByte}-${endByte}`
* @returns object
*/
export async function downloadFile(filePath: string, range?: string) {
return request({
url: `/file/download/${encode(filePath)}`,
method: 'get',
headers: range ? { range } : {},
responseType: 'blob',
});
}
/**
* 下载文件切片
* @param filePath 文件路径带/,如:/upload/default/2023/06/xx.png
* @param chunkSize 数据块大小MB默认1MB
* @returns bolb
*/
export async function downloadFileChunk(
filePath: string,
chunkSize: number = 1
): Promise<Blob> {
chunkSize = chunkSize * 1024 * 1024;
let start = 0; // 文件块的起始字节
let end = chunkSize - 1; // 文件块的结束字节
let totalSize = 0; // 文件总大小
let downloadedSize = 0; // 已下载的文件大小
let filePart: Blob[] = []; // 文件数据块内容
// 发送带有 Range 请求头的 HTTP 请求
async function sendRequest() {
const range = `bytes=${start}-${end}`;
const res = await downloadFile(filePath, range);
if (res.code === 200 && res.status === 206) {
// 总大小
const contentRange = res.headers.get('content-range') || '0/0';
totalSize = parseInt(contentRange.split('/')[1]);
// 已下载大小
const contentLength = res.headers.get('content-length') || '0';
const chunkSize = parseInt(contentLength);
// 下一段数据块区间
start += chunkSize;
end = Math.min(start + chunkSize - 1, totalSize - 1);
// 记录下载结果
filePart.push(res.data);
downloadedSize += chunkSize;
// 小于总大小继续下载后续数据
if (downloadedSize < totalSize) {
await sendRequest();
}
} else {
return res;
}
}
await sendRequest();
return new Blob(filePart, { type: 'application/octet-stream' });
}
/**
* 上传文件
* @param data 表单数据对象
* @returns object
*/
export function uploadFile(data: FormData) {
return request({
url: '/file/upload',
method: 'post',
data,
dataType: 'form-data',
});
}
/**
* 上传切片文件
* @param file 文件对象
* @param chunkSize 数据块大小MB默认1MB
* @param subPath 归属子路径, 默认default
* @returns
*/
export async function uploadFileChunk(
fileData: File,
chunkSize: number = 1,
subPath: string = 'default'
) {
const { name, size } = fileData;
const chunkSizeInBytes = chunkSize * 1024 * 1024;
// 文件标识使用唯一编码 MD5(文件名+文件大小)
const fileIdentifier = `${name}-${size}`;
// 文件切分为多少份进行上传
const chunksCount = Math.ceil(size / chunkSizeInBytes);
// 切块的数据数据用于上传
const fileChunks: { index: number; chunk: Blob }[] = [];
for (let i = 0; i < chunksCount; i++) {
const start = i * chunkSizeInBytes;
const end = Math.min(start + chunkSizeInBytes, size);
fileChunks.push({
index: i,
chunk: fileData.slice(start, end),
});
}
// 检查是否已上传部分数据块
const resCheck = await chunkCheck(fileIdentifier, name);
if (resCheck.code !== 200) {
return resCheck;
}
let uploadedSize = 0;
let uploadProgress = 0;
for (const { index, chunk } of fileChunks) {
const chunksIndex = `${index}`;
// 跳过已上传块
if (resCheck.data.includes(chunksIndex)) {
continue;
}
// 上传数据块
const formData = new FormData();
formData.append('file', chunk, name);
formData.append('index', chunksIndex);
formData.append('identifier', fileIdentifier);
const resUpload = await chunkUpload(formData);
if (resUpload.code === 200) {
uploadedSize += chunk.size;
uploadProgress = (uploadedSize / size) * 100;
console.log(`上传进度:${uploadProgress}%`);
} else {
// 上传失败处理
break;
}
}
// 上传数据完整后合并数据块
if (uploadedSize === size) {
return await chunkMerge(fileIdentifier, name, subPath);
}
return { code: 500, msg: '上传出错,请重试' };
}
/**
* 切片文件检查
* @param identifier 文件标识
* @param fileName 原文件名称
* @returns object
*/
export function chunkCheck(identifier: string, fileName: string) {
return request({
url: '/file/chunkCheck',
method: 'post',
data: { identifier, fileName },
});
}
/**
* 切片文件合并
* @param identifier 文件标识
* @param fileName 原文件名称
* @param subPath 文件归属
* @returns object
*/
export function chunkMerge(
identifier: string,
fileName: string,
subPath: string = 'default'
) {
return request({
url: '/file/chunkMerge',
method: 'post',
data: { identifier, fileName, subPath },
});
}
/**
* 切片文件上传
* @param data 表单数据对象
* @returns object
*/
export function chunkUpload(data: FormData) {
return request({
url: '/file/chunkUpload',
method: 'post',
data,
dataType: 'form-data',
});
}