240 lines
6.8 KiB
TypeScript
240 lines
6.8 KiB
TypeScript
import { getAliyunKeyApi, insertFileApi } from "@/service/file";
|
|
import { Message } from "@arco-design/web-vue";
|
|
import { showUploadProgress, updateUploadProgress, closeUploadProgress } from "@/components/upload-progress/index";
|
|
|
|
export interface UploadImageResponse {
|
|
name: string;
|
|
size: string;
|
|
url: string;
|
|
blob: Blob;
|
|
}
|
|
|
|
export const uploadImage = (
|
|
options: {
|
|
quality?: number;
|
|
mimeType?: string;
|
|
} = {}
|
|
): Promise<UploadImageResponse> => {
|
|
const { quality = 0.8, mimeType = "image/webp" } = options;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof document === "undefined") {
|
|
reject(new Error("Require browser environment"));
|
|
return;
|
|
}
|
|
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = "image/*";
|
|
input.style.display = "none";
|
|
|
|
const processImage = (file: File): Promise<UploadImageResponse> => {
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image();
|
|
const objectURL = URL.createObjectURL(file);
|
|
|
|
img.onload = () => {
|
|
try {
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) throw new Error("Canvas init failed");
|
|
|
|
canvas.width = img.width;
|
|
canvas.height = img.height;
|
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
|
|
URL.revokeObjectURL(objectURL);
|
|
|
|
canvas.toBlob(
|
|
(blob) => {
|
|
if (!blob) {
|
|
reject(new Error("Compression failed"));
|
|
return;
|
|
}
|
|
resolve({ name: file.name, size: blob.size.toString(), url: URL.createObjectURL(blob), blob: blob });
|
|
},
|
|
mimeType,
|
|
quality
|
|
);
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
};
|
|
|
|
img.onerror = () => {
|
|
URL.revokeObjectURL(objectURL);
|
|
reject(new Error("Image load failed"));
|
|
};
|
|
|
|
img.src = objectURL;
|
|
});
|
|
};
|
|
|
|
input.onchange = async (event) => {
|
|
const file = (event.target as HTMLInputElement).files?.[0];
|
|
if (!file) {
|
|
reject(new Error("No file selected"));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (!file.type.startsWith("image/")) {
|
|
throw new Error("Invalid image file");
|
|
}
|
|
|
|
const result = await processImage(file);
|
|
resolve(result);
|
|
} catch (error) {
|
|
reject(error);
|
|
} finally {
|
|
document.body.removeChild(input);
|
|
}
|
|
};
|
|
|
|
document.body.appendChild(input);
|
|
input.click();
|
|
});
|
|
};
|
|
export interface UploadVideoResponse {
|
|
name: string;
|
|
size: string;
|
|
url: string;
|
|
blob: Blob;
|
|
}
|
|
|
|
export const uploadVideo = (): Promise<UploadVideoResponse> => {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof document === "undefined") {
|
|
reject(new Error("Require browser environment"));
|
|
return;
|
|
}
|
|
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = "video/*";
|
|
input.style.display = "none";
|
|
|
|
input.onchange = (event) => {
|
|
const file = (event.target as HTMLInputElement).files?.[0];
|
|
if (!file) {
|
|
reject(new Error("No file selected"));
|
|
return;
|
|
}
|
|
|
|
if (!file.type.startsWith("video/")) {
|
|
reject(new Error("Invalid video file"));
|
|
return;
|
|
}
|
|
|
|
resolve({
|
|
name: file.name,
|
|
size: file.size.toString(),
|
|
url: URL.createObjectURL(file),
|
|
blob: file
|
|
});
|
|
|
|
document.body.removeChild(input);
|
|
};
|
|
|
|
document.body.appendChild(input);
|
|
input.click();
|
|
});
|
|
};
|
|
|
|
export interface UploadToAliyunResponse {
|
|
url: string;
|
|
fileName: string;
|
|
fileSize: string;
|
|
}
|
|
|
|
export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, onProgress?: (percent: number) => void): Promise<UploadToAliyunResponse> {
|
|
return new Promise((resolve, reject) => {
|
|
// 在开始上传时显示上传进度
|
|
showUploadProgress("uploading");
|
|
|
|
getAliyunKeyApi()
|
|
.then(async (res) => {
|
|
try {
|
|
const aliyunKey = res.data;
|
|
const formData = new FormData();
|
|
|
|
const fileExt = fileName.includes(".") ? fileName.slice(fileName.lastIndexOf(".")) : "";
|
|
const uniqueName = `sl_${Date.now()}_${Math.random().toString(36).slice(2)}${fileExt}`;
|
|
|
|
formData.append("key", `${aliyunKey.dir}${uniqueName}`);
|
|
formData.append("OSSAccessKeyId", aliyunKey.accessid);
|
|
formData.append("policy", aliyunKey.policy);
|
|
formData.append("Signature", aliyunKey.signature);
|
|
formData.append("file", blob, uniqueName);
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open("POST", aliyunKey.host, true);
|
|
|
|
// 监听上传进度
|
|
xhr.upload.onprogress = (event) => {
|
|
if (event.lengthComputable && onProgress) {
|
|
const percent = (event.loaded / event.total) * 100;
|
|
onProgress(parseFloat(percent.toFixed(2))); // 保留两位小数
|
|
updateUploadProgress(percent); // 更新进度条
|
|
}
|
|
};
|
|
|
|
xhr.onload = async () => {
|
|
if (xhr.status === 204 || xhr.status === 200) {
|
|
const allFilePath = `${aliyunKey.host}/${aliyunKey.dir}${uniqueName}`;
|
|
|
|
await insertFileApi({
|
|
fileName,
|
|
fileSize,
|
|
fileMapName: allFilePath
|
|
});
|
|
|
|
updateUploadProgress(100); // 上传完毕
|
|
Message.success("上传成功");
|
|
|
|
setTimeout(() => {
|
|
closeUploadProgress(); // 关闭上传进度条
|
|
}, 500);
|
|
|
|
resolve({
|
|
url: allFilePath,
|
|
fileName,
|
|
fileSize
|
|
});
|
|
} else {
|
|
updateUploadProgress(0, "failed"); // 上传失败
|
|
Message.error("上传失败,请稍后再试");
|
|
|
|
setTimeout(() => {
|
|
closeUploadProgress(); // 关闭上传进度条
|
|
}, 500);
|
|
|
|
reject(new Error(`OSS上传失败: ${xhr.responseText}`));
|
|
}
|
|
};
|
|
|
|
xhr.onerror = () => {
|
|
updateUploadProgress(0, "failed"); // 上传失败
|
|
Message.error("上传失败,请稍后再试");
|
|
|
|
setTimeout(() => {
|
|
closeUploadProgress(); // 关闭上传进度条
|
|
}, 500);
|
|
reject(new Error(`OSS上传异常: ${xhr.responseText}`));
|
|
};
|
|
|
|
xhr.send(formData);
|
|
} catch (error) {
|
|
updateUploadProgress(0, "failed"); // 上传失败
|
|
Message.error("上传失败,请稍后再试");
|
|
|
|
setTimeout(() => {
|
|
closeUploadProgress(); // 关闭上传进度条
|
|
}, 500);
|
|
reject(error);
|
|
}
|
|
})
|
|
.catch(reject);
|
|
});
|
|
}
|