112 lines
3.6 KiB
JavaScript
112 lines
3.6 KiB
JavaScript
/**
|
||
* 阿里云 OSS PostObject 直传(对齐 xk-admin oss-upload.ts)
|
||
*
|
||
* 运维:微信小程序 uploadFile 合法域名需包含 OSS host
|
||
* 例如 https://xiaokang88.oss-cn-hangzhou.aliyuncs.com
|
||
*/
|
||
import { getBusinessOssSignatureApi, unwrapOssSignature, registerBusinessOssFile } from '@/api/upload.js';
|
||
|
||
/**
|
||
* @param {string} filePath 本地临时文件路径
|
||
* @returns {string} xk_upload_YYYYMMDD/uuid_timestamp.ext
|
||
*/
|
||
export function generateObjectName(filePath) {
|
||
const today = new Date();
|
||
const year = today.getFullYear();
|
||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||
const day = String(today.getDate()).padStart(2, '0');
|
||
const dateStr = `${year}${month}${day}`;
|
||
|
||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||
const r = (Math.random() * 16) | 0;
|
||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||
return v.toString(16);
|
||
});
|
||
|
||
const timestamp = Math.floor(Date.now() / 1000);
|
||
const pathStr = String(filePath || '');
|
||
const lastDotIndex = pathStr.lastIndexOf('.');
|
||
const extension = lastDotIndex > 0 ? pathStr.substring(lastDotIndex + 1).toLowerCase() : 'jpg';
|
||
|
||
return `xk_upload_${dateStr}/${uuid}_${timestamp}.${extension}`;
|
||
}
|
||
|
||
function basenameFromPath(filePath) {
|
||
const pathStr = String(filePath || '');
|
||
const parts = pathStr.split(/[/\\]/);
|
||
return parts[parts.length - 1] || '';
|
||
}
|
||
|
||
/**
|
||
* @returns {Promise<{ accessKeyId, policy, signature, host, bucket, key, expire }>}
|
||
*/
|
||
export async function getOssSignatureFromBackend() {
|
||
const res = await getBusinessOssSignatureApi();
|
||
const signature = unwrapOssSignature(res);
|
||
if (!signature) {
|
||
throw new Error('获取 OSS 签名失败');
|
||
}
|
||
return signature;
|
||
}
|
||
|
||
/**
|
||
* @param {{ filePath: string, onProgress?: (percent: number) => void }} options
|
||
* @returns {Promise<{ url: string, objectName: string }>}
|
||
*/
|
||
export async function uploadToOss(options) {
|
||
const { filePath, onProgress, fileSize = 0, fileName = '' } = options || {};
|
||
if (!filePath) {
|
||
throw new Error('文件路径为空');
|
||
}
|
||
|
||
const signature = await getOssSignatureFromBackend();
|
||
const objectName = generateObjectName(filePath);
|
||
|
||
const formData = {
|
||
key: objectName,
|
||
policy: signature.policy,
|
||
OSSAccessKeyId: signature.accessKeyId,
|
||
signature: signature.signature,
|
||
success_action_status: '200',
|
||
'x-oss-object-acl': 'public-read',
|
||
};
|
||
|
||
return new Promise((resolve, reject) => {
|
||
const uploadTask = uni.uploadFile({
|
||
url: signature.host,
|
||
filePath,
|
||
name: 'file',
|
||
formData,
|
||
success: (uploadRes) => {
|
||
const code = uploadRes.statusCode;
|
||
if (code >= 200 && code < 300 || code === 204) {
|
||
const host = String(signature.host).replace(/\/$/, '');
|
||
const url = `${host}/${objectName}`;
|
||
const displayName = fileName || basenameFromPath(filePath);
|
||
registerBusinessOssFile(url, fileSize, displayName).catch((err) => {
|
||
console.warn('OSS 文件登记失败:', err);
|
||
});
|
||
resolve({
|
||
url,
|
||
objectName,
|
||
});
|
||
return;
|
||
}
|
||
reject(new Error(`OSS 上传失败,HTTP ${code}`));
|
||
},
|
||
fail: (err) => {
|
||
reject(err instanceof Error ? err : new Error((err && err.errMsg) || 'OSS 上传失败'));
|
||
},
|
||
});
|
||
|
||
if (onProgress && uploadTask && typeof uploadTask.onProgressUpdate === 'function') {
|
||
uploadTask.onProgressUpdate((res) => {
|
||
if (res.totalBytesExpectedToSend > 0) {
|
||
const percent = Math.round((res.totalBytesSent / res.totalBytesExpectedToSend) * 100);
|
||
onProgress(percent);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|