useUpload.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import * as FileApi from '@/api/infra/file'
  2. import CryptoJS from 'crypto-js'
  3. import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
  4. import axios from 'axios'
  5. /**
  6. * 获得上传 URL
  7. */
  8. export const getUploadUrl = (): string => {
  9. return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/infra/file/upload'
  10. }
  11. export const getUploadUrlOnlyPath = (): string => {
  12. return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/infra/file/upload/path'
  13. }
  14. export const useUpload = () => {
  15. // 后端上传地址
  16. const uploadUrl = getUploadUrl()
  17. const uploadUrlPath = getUploadUrlOnlyPath()
  18. // 是否使用前端直连上传
  19. const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
  20. // 重写ElUpload上传方法
  21. const httpRequest = async (options: UploadRequestOptions) => {
  22. // 模式一:前端上传
  23. if (isClientUpload) {
  24. // 1.1 生成文件名称
  25. const fileName = await generateFileName(options.file)
  26. // 1.2 获取文件预签名地址
  27. const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
  28. // 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
  29. return axios
  30. .put(presignedInfo.uploadUrl, options.file, {
  31. headers: {
  32. 'Content-Type': options.file.type
  33. }
  34. })
  35. .then(() => {
  36. // 1.4. 记录文件信息到后端(异步)
  37. createFile(presignedInfo, fileName, options.file)
  38. // 通知成功,数据格式保持与后端上传的返回结果一致
  39. return { data: presignedInfo.url }
  40. })
  41. } else {
  42. // 模式二:后端上传
  43. // 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
  44. return new Promise((resolve, reject) => {
  45. FileApi.updateFile({ file: options.file })
  46. .then((res) => {
  47. if (res.code === 0) {
  48. resolve(res)
  49. } else {
  50. reject(res)
  51. }
  52. })
  53. .catch((res) => {
  54. reject(res)
  55. })
  56. })
  57. }
  58. }
  59. const httpRequestOnlyPath = async (options: UploadRequestOptions) => {
  60. // 模式一:前端上传
  61. if (isClientUpload) {
  62. // 1.1 生成文件名称
  63. const fileName = await generateFileName(options.file)
  64. // 1.2 获取文件预签名地址
  65. const presignedInfoPath = await FileApi.getFilePresignedUrl(fileName)
  66. // 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
  67. return axios
  68. .put(presignedInfoPath.uploadUrlPath, options.file, {
  69. headers: {
  70. 'Content-Type': options.file.type
  71. }
  72. })
  73. .then(() => {
  74. // 1.4. 记录文件信息到后端(异步)
  75. createFile(presignedInfoPath, fileName, options.file)
  76. // 通知成功,数据格式保持与后端上传的返回结果一致
  77. return { data: presignedInfoPath.url }
  78. })
  79. } else {
  80. // 模式二:后端上传
  81. // 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
  82. return new Promise((resolve, reject) => {
  83. FileApi.updateFilePath({ file: options.file })
  84. // FileApi.updateFile({ file: options.file })
  85. .then((res) => {
  86. if (res.code === 0) {
  87. resolve(res)
  88. } else {
  89. reject(res)
  90. }
  91. })
  92. .catch((res) => {
  93. reject(res)
  94. })
  95. })
  96. }
  97. }
  98. return {
  99. uploadUrl,
  100. uploadUrlPath,
  101. httpRequest,
  102. httpRequestOnlyPath
  103. }
  104. }
  105. /**
  106. * 创建文件信息
  107. * @param vo 文件预签名信息
  108. * @param name 文件名称
  109. * @param file 文件
  110. */
  111. function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
  112. const fileVo = {
  113. configId: vo.configId,
  114. url: vo.url,
  115. path: name,
  116. name: file.name,
  117. type: file.type,
  118. size: file.size
  119. }
  120. FileApi.createFile(fileVo)
  121. return fileVo
  122. }
  123. /**
  124. * 生成文件名称(使用算法SHA256)
  125. * @param file 要上传的文件
  126. */
  127. async function generateFileName(file: UploadRawFile) {
  128. // 读取文件内容
  129. const data = await file.arrayBuffer()
  130. const wordArray = CryptoJS.lib.WordArray.create(data)
  131. // 计算SHA256
  132. const sha256 = CryptoJS.SHA256(wordArray).toString()
  133. // 拼接后缀
  134. const ext = file.name.substring(file.name.lastIndexOf('.'))
  135. return `${sha256}${ext}`
  136. }
  137. /**
  138. * 上传类型
  139. */
  140. enum UPLOAD_TYPE {
  141. // 客户端直接上传(只支持S3服务)
  142. CLIENT = 'client',
  143. // 客户端发送到后端上传
  144. SERVER = 'server'
  145. }