uniapp-ZHSQ/utils/uploadOSS.js

153 lines
5.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// uniapp-ZHSQ —— OSS 直传客户端
//
// 用法:
// import { uploadOSS } from '@/utils/uploadOSS'
// const { objectKey, url } = await uploadOSS({ filePath: tempFilePath, scene: 'avatar' })
// // objectKey: OSS 上的相对 key存数据库
// // url: 公开访问 URL如果走签名 URL 显示,前端再单独处理)
//
// 支持的 scene 由服务端 wechatOssSceneCfg 控制,目前:
// - avatar 用户头像 (jpg/jpeg/png/webp<=5MB)
// - merchant 商家入驻资料 (jpg/jpeg/png/webp/pdf<=10MB)
import { RequsetUrl, aliyunOssUrl } from "@/utils/index.js";
const inferExt = (filePath) => {
if (!filePath) return "";
const idx = filePath.lastIndexOf(".");
if (idx < 0) return "";
return filePath.substring(idx + 1).toLowerCase();
};
const fetchCredential = (scene, ext, bindId) => {
return new Promise((resolve, reject) => {
const data = { scene, ext };
if (bindId) data.bind_id = bindId; // 客服聊天场景:按会话分目录
uni.request({
url: RequsetUrl + "/api/v1/wechat/oss/credential",
method: "POST",
header: {
Authorization: uni.getStorageSync("ctoken") || "",
"Content-Type": "application/json",
},
data,
success: (res) => {
if (res.statusCode !== 200) return reject(new Error("获取上传凭证失败:" + res.statusCode));
const body = res.data || {};
// 兼容服务端通用响应包装
const data = body.data || body;
if (!data || !data.host) return reject(new Error("上传凭证字段缺失"));
resolve(data);
},
fail: (err) => reject(err),
});
});
};
const postToOSS = (filePath, cred) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: cred.host,
filePath,
name: "file", // 必须是 fileOSS PostObject 协议约定
formData: {
key: cred.object_key,
OSSAccessKeyId: cred.access_key_id,
"x-oss-security-token": cred.security_token,
policy: cred.policy,
signature: cred.signature,
success_action_status: "200",
},
success: (res) => {
// OSS 200 -> 空 body 视为成功
if (res.statusCode === 200 || res.statusCode === 204) {
resolve();
} else {
reject(new Error("OSS 上传失败:" + res.statusCode + " " + (res.data || "")));
}
},
fail: (err) => reject(err),
});
});
};
/**
* 直传 OSS。
* @param {Object} opts
* @param {string} opts.filePath 本地文件临时路径uni.chooseImage 拿到的 path
* @param {string} opts.scene 上传场景avatar / merchant
* @param {string} [opts.ext] 扩展名(不含点);不传则从 filePath 推断
* @param {boolean} [opts.showLoading=true]
* @returns {Promise<{ objectKey: string, url: string }>}
*/
export const uploadOSS = async ({ filePath, scene, ext, bindId, showLoading = true }) => {
if (!filePath) throw new Error("filePath 不能为空");
if (!scene) throw new Error("scene 不能为空");
const finalExt = (ext || inferExt(filePath)).toLowerCase();
if (!finalExt) throw new Error("无法识别文件扩展名");
if (showLoading) uni.showLoading({ title: "上传中", mask: true });
try {
const cred = await fetchCredential(scene, finalExt, bindId);
await postToOSS(filePath, cred);
return {
objectKey: cred.object_key,
url: aliyunOssUrl + "/" + cred.object_key,
};
} finally {
if (showLoading) uni.hideLoading();
}
};
/**
* 兼容旧版 utils/index.js 中 upload(filename, fn) 的回调式 API。
* 内部走 OSS 直传,回调 res 形状对齐旧版res.data.path 为以 / 开头的 object key
* 调用方 picUrl + res.data.path 可以直接拼成完整 URL
*
* @param {string} filePath
* @param {Function} fn 接收 { code, msg, data: { path } }
* @param {string} [scene='merchant']
*/
export const uploadOSSCompat = (filePath, fn, scene = "merchant") => {
uploadOSS({ filePath, scene })
.then(({ objectKey }) => {
fn && fn({ code: 1, msg: "ok", data: { path: "/" + objectKey } });
})
.catch((err) => {
console.error("uploadOSSCompat 失败:", err);
uni.showToast({ title: err.message || "上传文件失败", icon: "none" });
});
};
/**
* 取私密文件查看 URL小程序商家本人查看自己上传的证件 / 合同)。
* 服务端会校验 path 必须以 wechat/merchant_private/{当前 user_id}/ 开头。
*
* @param {string} objectKey 存数据库的 path带或不带前导 /
* @returns {Promise<{ url: string, expireSeconds: number }>}
*/
export const signPrivateView = (objectKey) => {
return new Promise((resolve, reject) => {
if (!objectKey) return reject(new Error("object_key 不能为空"));
const cleanKey = objectKey.replace(/^\/+/, "");
uni.request({
url: RequsetUrl + "/api/v1/wechat/oss/sign-private-view",
method: "POST",
header: {
Authorization: uni.getStorageSync("ctoken") || "",
"Content-Type": "application/json",
},
data: { object_key: cleanKey },
success: (res) => {
if (res.statusCode !== 200) return reject(new Error("签发失败:" + res.statusCode));
const body = res.data || {};
const data = body.data || body;
if (!data || !data.url) return reject(new Error("签发结果字段缺失"));
resolve({ url: data.url, expireSeconds: data.expire_seconds });
},
fail: (err) => reject(err),
});
});
};