teamBackgroundFile.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { BaseApi } from './baseApi.js'
  2. import api from './index.js'
  3. const OPEN_DOCUMENT_EXTENSIONS = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
  4. const TEXT_PREVIEW_EXTENSIONS = ['txt', 'md', 'csv']
  5. const MAX_TEXT_PREVIEW_SIZE = 256 * 1024
  6. function parseResult(response) {
  7. let result = response && response.data
  8. if (typeof result === 'string') {
  9. try {
  10. result = JSON.parse(result)
  11. } catch (error) {
  12. throw new Error('上传失败,请重试')
  13. }
  14. }
  15. if (!response || response.statusCode !== 200 || !result || result.code !== 0) {
  16. throw new Error(result && result.msg ? result.msg : '上传失败,请重试')
  17. }
  18. return result.data
  19. }
  20. function unwrap(response) {
  21. const result = response && response.data
  22. if (!result || result.code !== 0) {
  23. throw new Error(result && result.msg ? result.msg : '请求失败,请重试')
  24. }
  25. return result.data
  26. }
  27. function getHeader(headers, name) {
  28. if (!headers || typeof headers !== 'object') return ''
  29. const expectedName = name.toLowerCase()
  30. for (const key of Object.keys(headers)) {
  31. if (key.toLowerCase() === expectedName) return String(headers[key] || '')
  32. }
  33. return ''
  34. }
  35. function isSafeAttachment(headers) {
  36. const contentType = getHeader(headers, 'content-type').split(';')[0].trim().toLowerCase()
  37. const disposition = getHeader(headers, 'content-disposition').trim()
  38. if (!contentType || !/^attachment(?:\s*;|$)/i.test(disposition)) return false
  39. if (contentType === 'application/json' || contentType === 'text/json'
  40. || contentType.endsWith('+json') || contentType === 'text/html'
  41. || contentType === 'application/xhtml+xml') return false
  42. return true
  43. }
  44. function getDownloadFileName(headers) {
  45. const disposition = getHeader(headers, 'content-disposition')
  46. const encodedMatch = disposition.match(/(?:^|;)\s*filename\*\s*=\s*UTF-8''([^;]*)/i)
  47. if (encodedMatch) {
  48. try {
  49. const decoded = decodeURIComponent(encodedMatch[1].trim())
  50. if (decoded) return decoded
  51. } catch (error) {
  52. // Fall back to the compatible ASCII filename below.
  53. }
  54. }
  55. const fallbackMatch = disposition.match(/(?:^|;)\s*filename\s*=\s*(?:"([^"]*)"|([^;]*))/i)
  56. return fallbackMatch ? String(fallbackMatch[1] || fallbackMatch[2] || '').trim() : ''
  57. }
  58. function getFileExtension(fileName) {
  59. const extensionIndex = fileName.lastIndexOf('.')
  60. if (extensionIndex < 0 || extensionIndex === fileName.length - 1) return ''
  61. return fileName.slice(extensionIndex + 1).toLowerCase()
  62. }
  63. function readTextPreview(filePath, fileName) {
  64. return new Promise((resolve, reject) => {
  65. const fail = () => reject(new Error('文件读取失败,请重试'))
  66. let fileSystem
  67. try {
  68. fileSystem = typeof wx !== 'undefined' && typeof wx.getFileSystemManager === 'function'
  69. ? wx.getFileSystemManager()
  70. : null
  71. } catch (error) {
  72. fail()
  73. return
  74. }
  75. if (!fileSystem || typeof fileSystem.getFileInfo !== 'function'
  76. || typeof fileSystem.readFile !== 'function') {
  77. fail()
  78. return
  79. }
  80. try {
  81. fileSystem.getFileInfo({
  82. filePath,
  83. success: info => {
  84. const size = Number(info && info.size)
  85. if (!Number.isFinite(size) || size < 0) {
  86. fail()
  87. return
  88. }
  89. if (size === 0) {
  90. resolve({ mode: 'text', fileName, content: '', truncated: false })
  91. return
  92. }
  93. try {
  94. fileSystem.readFile({
  95. filePath,
  96. encoding: 'utf8',
  97. position: 0,
  98. length: Math.min(size, MAX_TEXT_PREVIEW_SIZE),
  99. success: result => {
  100. if (!result || typeof result.data !== 'string') {
  101. fail()
  102. return
  103. }
  104. resolve({
  105. mode: 'text',
  106. fileName,
  107. content: result.data,
  108. truncated: size > MAX_TEXT_PREVIEW_SIZE
  109. })
  110. },
  111. fail
  112. })
  113. } catch (error) {
  114. fail()
  115. }
  116. },
  117. fail
  118. })
  119. } catch (error) {
  120. fail()
  121. }
  122. })
  123. }
  124. export function uploadTeamBackgroundFile(teamId, filePath, onProgress = () => {}) {
  125. return new Promise((resolve, reject) => {
  126. const task = uni.uploadFile({
  127. url: `${BaseApi}/core/user/team/${teamId}/background-files`,
  128. filePath,
  129. name: 'file',
  130. header: { token: uni.getStorageSync('token') },
  131. success: response => {
  132. try {
  133. resolve(parseResult(response))
  134. } catch (error) {
  135. reject(error)
  136. }
  137. },
  138. fail: reject
  139. })
  140. task.onProgressUpdate(progress => onProgress(progress.progress))
  141. })
  142. }
  143. export const listTeamBackgroundFiles = teamId => api
  144. .get(`/core/user/team/${teamId}/background-files`, {}, false)
  145. .then(unwrap)
  146. export const disableTeamBackgroundFile = (teamId, fileId) => api
  147. .del(`/core/user/team/${teamId}/background-files/${fileId}`, {}, false)
  148. .then(unwrap)
  149. export const createTeamBackgroundSession = teamId => api
  150. .post(`/core/user/team/${teamId}/background-upload-session`, {}, false)
  151. .then(unwrap)
  152. export function downloadTeamBackgroundFile(teamId, fileId) {
  153. return new Promise((resolve, reject) => {
  154. let responseHeaders
  155. const task = uni.downloadFile({
  156. url: `${BaseApi}/core/user/team/${teamId}/background-files/${fileId}/download`,
  157. header: { token: uni.getStorageSync('token') },
  158. success: result => {
  159. const resultHeaders = result && (result.header || result.headers)
  160. const headers = responseHeaders || resultHeaders
  161. if (!result || result.statusCode !== 200
  162. || !isSafeAttachment(headers)) {
  163. reject(new Error('下载失败,请重试'))
  164. return
  165. }
  166. const fileName = getDownloadFileName(headers)
  167. const extension = getFileExtension(fileName)
  168. if (OPEN_DOCUMENT_EXTENSIONS.includes(extension)) {
  169. resolve({ mode: 'document', fileName, filePath: result.tempFilePath })
  170. return
  171. }
  172. if (TEXT_PREVIEW_EXTENSIONS.includes(extension)) {
  173. readTextPreview(result.tempFilePath, fileName).then(resolve, reject)
  174. return
  175. }
  176. reject(new Error('文件类型不支持预览'))
  177. },
  178. fail: reject
  179. })
  180. if (task && typeof task.onHeadersReceived === 'function') {
  181. task.onHeadersReceived(result => {
  182. responseHeaders = result && (result.header || result.headers)
  183. })
  184. }
  185. })
  186. }