index.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import { config } from "@/config";
  2. import { request } from "../service";
  3. import { basename, extname, filename, parse, parseObject, pathJoin, uuid } from "../utils";
  4. import { useStore } from "../store";
  5. // 上传进度回调结果类型
  6. export type OnProgressUpdateResult = {
  7. progress: number;
  8. totalBytesSent: number;
  9. totalBytesExpectedToSend: number;
  10. };
  11. // 上传任务类型定义
  12. export type UploadTask = {
  13. abort(): void;
  14. };
  15. // 上传选项类型定义
  16. export type UploadOptions = {
  17. onProgressUpdate?: (result: OnProgressUpdateResult) => void; // 上传进度回调
  18. onTask?: (task: UploadTask) => void; // 上传任务回调
  19. };
  20. // 上传模式类型
  21. export type UploadMode = {
  22. mode: "local" | "cloud"; // 上传模式:本地或云端
  23. type: string; // 云服务类型
  24. };
  25. // 上传请求的参数类型
  26. export type UploadRequestOptions = {
  27. url: string;
  28. preview?: string;
  29. data: any;
  30. };
  31. // 云上传返回数据类型
  32. export type CloudUploadResponse = {
  33. uploadUrl?: string;
  34. url?: string;
  35. host?: string;
  36. credentials?: any;
  37. OSSAccessKeyId?: string;
  38. policy?: string;
  39. signature?: string;
  40. publicDomain?: string;
  41. token?: string;
  42. fields?: any;
  43. };
  44. // 本地上传返回数据类型
  45. export type LocalUploadResponse = {
  46. code: number;
  47. message?: string;
  48. data: string;
  49. };
  50. // 获取上传模式(本地/云端及云类型)
  51. async function getUploadMode(): Promise<UploadMode> {
  52. const res = await request({
  53. url: "/app/base/comm/uploadMode"
  54. });
  55. return parse<UploadMode>(res!)!;
  56. }
  57. /**
  58. * 路径上传
  59. * @param path 文件路径
  60. */
  61. export async function upload(path: string) {
  62. return uploadFile({
  63. path,
  64. size: 0,
  65. name: "",
  66. type: "image/png"
  67. });
  68. }
  69. /**
  70. * 文件上传
  71. * @param file 文件信息 ChooseImageTempFile
  72. * @param options 上传选项
  73. */
  74. export async function uploadFile(
  75. file: ChooseImageTempFile,
  76. options: UploadOptions | null = null
  77. ): Promise<string> {
  78. const { user } = useStore();
  79. // 获取上传模式和类型
  80. const { mode, type } = await getUploadMode();
  81. // 判断是否本地上传
  82. const isLocal = mode == "local";
  83. // 判断是否是云上传
  84. const isCloud = mode == "cloud";
  85. // 获取文件路径
  86. const filePath = file.path;
  87. // 获取文件名
  88. let fileName = file.name;
  89. // 如果文件名不存在,则使用文件路径的文件名
  90. if (fileName == "" || fileName == null) {
  91. fileName = basename(filePath);
  92. }
  93. // 获取文件扩展名
  94. let ext = extname(fileName);
  95. if (ext == "") {
  96. ext = "png";
  97. }
  98. // 生成唯一key: 原文件名_uuid.扩展名
  99. let key = `${filename(fileName)}_${uuid()}.${ext}`;
  100. // 云上传需要加上时间戳路径
  101. if (isCloud) {
  102. key = `app/${Date.now()}/${key}`;
  103. }
  104. // 支持多种上传方式
  105. return new Promise((resolve, reject) => {
  106. /**
  107. * 实际上传文件的函数
  108. * @param param0 上传参数
  109. */
  110. function next({ url, preview, data }: UploadRequestOptions) {
  111. // 发起上传请求
  112. const task = uni.uploadFile({
  113. url,
  114. filePath,
  115. name: "file",
  116. header: {
  117. // 本地上传带token
  118. Authorization: isLocal ? user.token : null
  119. },
  120. formData: {
  121. ...(data as UTSJSONObject),
  122. key
  123. },
  124. success(res) {
  125. if (isLocal) {
  126. // 本地上传返回处理
  127. const { code, data, message } = parseObject<LocalUploadResponse>(res.data)!;
  128. if (code == 1000) {
  129. resolve(data);
  130. } else {
  131. reject(message);
  132. }
  133. } else {
  134. // 云上传直接拼接url
  135. resolve(pathJoin(preview ?? url, key!));
  136. }
  137. },
  138. fail(err) {
  139. console.error(err);
  140. reject(err);
  141. }
  142. });
  143. // 上传任务回调
  144. if (options?.onTask != null) {
  145. options.onTask!({
  146. abort: () => {
  147. task.abort();
  148. }
  149. } as UploadTask);
  150. }
  151. // 上传进度回调
  152. if (options?.onProgressUpdate != null) {
  153. task.onProgressUpdate((result) => {
  154. const { progress, totalBytesSent, totalBytesExpectedToSend } = result;
  155. options.onProgressUpdate!({
  156. progress,
  157. totalBytesSent,
  158. totalBytesExpectedToSend
  159. });
  160. });
  161. }
  162. }
  163. // 本地上传
  164. if (isLocal) {
  165. next({
  166. url: config.baseUrl + "/app/base/comm/upload",
  167. data: {}
  168. });
  169. } else {
  170. // 云上传
  171. const data = {} as UTSJSONObject;
  172. // AWS需要提前传key
  173. if (type == "aws") {
  174. data.key = key;
  175. }
  176. // 获取云上传参数
  177. request({
  178. url: "/app/base/comm/upload",
  179. method: "POST",
  180. data
  181. })
  182. .then((res) => {
  183. const d = parse<CloudUploadResponse>(res!)!;
  184. switch (type) {
  185. // 腾讯云COS
  186. case "cos":
  187. next({
  188. url: d.url!,
  189. data: d.credentials!
  190. });
  191. break;
  192. // 阿里云OSS
  193. case "oss":
  194. next({
  195. url: d.host!,
  196. data: {
  197. OSSAccessKeyId: d.OSSAccessKeyId,
  198. policy: d.policy,
  199. signature: d.signature
  200. }
  201. });
  202. break;
  203. // 七牛云
  204. case "qiniu":
  205. next({
  206. url: d.uploadUrl!,
  207. preview: d.publicDomain,
  208. data: {
  209. token: d.token
  210. }
  211. });
  212. break;
  213. // 亚马逊AWS
  214. case "aws":
  215. next({
  216. url: d.url!,
  217. data: d.fields!
  218. });
  219. break;
  220. }
  221. })
  222. .catch(reject);
  223. }
  224. });
  225. }