test uploaded file types by file extension
This commit is contained in:
		
							parent
							
								
									bc00d6f295
								
							
						
					
					
						commit
						22d79ffc06
					
				
							
								
								
									
										58
									
								
								unitool.js
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								unitool.js
									
									
									
									
									
								
							@ -1,4 +1,7 @@
 | 
				
			|||||||
// uniapp people side tools
 | 
					// uniapp client tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const crypto = require('crypto')
 | 
				
			||||||
 | 
					const path = require('path')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//import './ican-H5Api.js' // 对齐H5Api: https://ext.dcloud.net.cn/plugin?id=415 // 注意要取消默认自带的 showToast https://uniapp.dcloud.io/api/system/clipboard?id=%e6%b3%a8%e6%84%8f
 | 
					//import './ican-H5Api.js' // 对齐H5Api: https://ext.dcloud.net.cn/plugin?id=415 // 注意要取消默认自带的 showToast https://uniapp.dcloud.io/api/system/clipboard?id=%e6%b3%a8%e6%84%8f
 | 
				
			||||||
const BASE_TYPE_DEFAULT = 'SERVER' // one of { SERVER: 服务器, UNICLOUD_FUNC: 云函数, UNICLOUD_OBJECT: 云对象 }
 | 
					const BASE_TYPE_DEFAULT = 'SERVER' // one of { SERVER: 服务器, UNICLOUD_FUNC: 云函数, UNICLOUD_OBJECT: 云对象 }
 | 
				
			||||||
@ -7,10 +10,16 @@ const my = {
 | 
				
			|||||||
    // getCurrentPages() 在 topWindow/App.vue 里有可能为空,所以用 getApp().$store 更安全. 20230513: 发现在微信小程序模拟器里,getApp().$store.state 未定义,所以还是用 globalThis.wo?.ss
 | 
					    // getCurrentPages() 在 topWindow/App.vue 里有可能为空,所以用 getApp().$store 更安全. 20230513: 发现在微信小程序模拟器里,getApp().$store.state 未定义,所以还是用 globalThis.wo?.ss
 | 
				
			||||||
    return globalThis.wo?.ss?.i18n?.mylang || getApp()?.$store?.state?.i18n?.mylang
 | 
					    return globalThis.wo?.ss?.i18n?.mylang || getApp()?.$store?.state?.i18n?.mylang
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  isTextFile (ext = '') {
 | 
				
			||||||
 | 
					    return (wo.envar.textExtensionList || ['txt', 'text']).includes(ext.replace(/^\./, '').toLowerCase())
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  isImageFile (ext = '') {
 | 
				
			||||||
 | 
					    return (wo.envar.imageExtensionList || ['jpg', 'jpeg', 'png', 'gif', 'webp', 'image']).includes(ext.replace(/^\./, '').toLowerCase())
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  isVideoFile (ext = '') {
 | 
				
			||||||
 | 
					    return (wo.envar.videoExtensionList || ['avi', 'mp4', 'mov', 'wmv', 'video']).includes(ext.replace(/^\./, '').toLowerCase())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
const crypto = require('crypto')
 | 
					 | 
				
			||||||
const path = require('path')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  // 用直观的色彩,代替语义化的类型:
 | 
					  // 用直观的色彩,代替语义化的类型:
 | 
				
			||||||
@ -293,20 +302,22 @@ module.exports = {
 | 
				
			|||||||
  } = {}) {
 | 
					  } = {}) {
 | 
				
			||||||
    // 有的管理后台不需要登录就允许上传,例如 cmctoy。因此不要在这里依赖登录状态。
 | 
					    // 有的管理后台不需要登录就允许上传,例如 cmctoy。因此不要在这里依赖登录状态。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let filePath, fileSize
 | 
					    let filePath, fileSize, filePicked
 | 
				
			||||||
    if (mediaType === 'image') {
 | 
					    if (mediaType === 'image') {
 | 
				
			||||||
      let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
 | 
					      let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
 | 
				
			||||||
      if (errorChoose) {
 | 
					      if (errorChoose) {
 | 
				
			||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = tempFiles?.[0]?.size
 | 
					      fileSize = tempFiles?.[0]?.size
 | 
				
			||||||
 | 
					      filePicked = tempFiles?.[0]
 | 
				
			||||||
      filePath = tempFilePaths?.[0]
 | 
					      filePath = tempFilePaths?.[0]
 | 
				
			||||||
    } else if (mediaType === 'video') {
 | 
					    } else if (mediaType === 'video') {
 | 
				
			||||||
      let [errorChoose, { tempFilePath, size, duration }] = await uni.chooseVideo({ sourceType })
 | 
					      let [errorChoose, { tempFilePath, tempFile, size, duration, }] = await uni.chooseVideo({ sourceType })
 | 
				
			||||||
      if (errorChoose) {
 | 
					      if (errorChoose) {
 | 
				
			||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = size
 | 
					      fileSize = size
 | 
				
			||||||
 | 
					      filePicked = tempFile
 | 
				
			||||||
      filePath = tempFilePath
 | 
					      filePath = tempFilePath
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // #ifdef WEB
 | 
					      // #ifdef WEB
 | 
				
			||||||
@ -316,11 +327,8 @@ module.exports = {
 | 
				
			|||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '文件选择失败。请稍后再试,或向客服投诉。', enUS: 'File choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '文件选择失败。请稍后再试,或向客服投诉。', enUS: 'File choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = tempFiles?.[0]?.size
 | 
					      fileSize = tempFiles?.[0]?.size
 | 
				
			||||||
 | 
					      filePicked = tempFiles?.[0]
 | 
				
			||||||
      filePath = tempFilePaths?.[0]
 | 
					      filePath = tempFilePaths?.[0]
 | 
				
			||||||
      let fileExt = path.extname(tempFiles?.[0]?.name?.toLowerCase?.() || '')
 | 
					 | 
				
			||||||
      if (Array.isArray(mediaType) && !mediaType?.includes?.(fileExt)) {
 | 
					 | 
				
			||||||
        return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '不支持的文件后缀\n' + fileExt, enUS: 'Unsupported file extension\n' + fileExt } }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // #endif
 | 
					      // #endif
 | 
				
			||||||
      // #ifndef WEB
 | 
					      // #ifndef WEB
 | 
				
			||||||
      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '请切换到网页端上传文件!', enUS: 'Please switch to WebApp to upload files.' } }
 | 
					      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '请切换到网页端上传文件!', enUS: 'Please switch to WebApp to upload files.' } }
 | 
				
			||||||
@ -334,6 +342,13 @@ module.exports = {
 | 
				
			|||||||
      return { _state: 'CER_FILE_TOO_LARGE', _msg: { zhCN: `文件太大了,无法上传。最大允许 ${sizeLimitMB}`, enUS: `File too large to upload. Maximum allowed is ${sizeLimitMB}` } }
 | 
					      return { _state: 'CER_FILE_TOO_LARGE', _msg: { zhCN: `文件太大了,无法上传。最大允许 ${sizeLimitMB}`, enUS: `File too large to upload. Maximum allowed is ${sizeLimitMB}` } }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let fileExt = filePicked?.name?.split?.('.')?.pop?.()?.toLowerCase?.()
 | 
				
			||||||
 | 
					    if (mediaType === 'image' && !my.isImageFile(fileExt)
 | 
				
			||||||
 | 
					      || mediaType === 'video' && !my.isVideoFile(fileExt)
 | 
				
			||||||
 | 
					      || Array.isArray(mediaType) && !mediaType?.includes?.('.' + fileExt)) {
 | 
				
			||||||
 | 
					      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '不支持的文件:\n' + filePicked?.name, enUS: 'Unsupported file:\n' + filePicked?.name } }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (filePath) {
 | 
					    if (filePath) {
 | 
				
			||||||
      for (let key in formData) {
 | 
					      for (let key in formData) {
 | 
				
			||||||
        // multer 不会自动处理 JSON 数据,必须前后端配合处理
 | 
					        // multer 不会自动处理 JSON 数据,必须前后端配合处理
 | 
				
			||||||
@ -374,13 +389,14 @@ module.exports = {
 | 
				
			|||||||
    //   return { _state: 'USER_OFFLINE', errMsg: 'offline user cannot upload files' }
 | 
					    //   return { _state: 'USER_OFFLINE', errMsg: 'offline user cannot upload files' }
 | 
				
			||||||
    // }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let filePath, cloudPath, fileSize
 | 
					    let filePath, cloudPath, fileSize, filePicked
 | 
				
			||||||
    if (mediaType === 'image') {
 | 
					    if (mediaType === 'image') {
 | 
				
			||||||
      let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
 | 
					      let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
 | 
				
			||||||
      if (errorChoose) {
 | 
					      if (errorChoose) {
 | 
				
			||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = tempFiles?.[0]?.size
 | 
					      fileSize = tempFiles?.[0]?.size
 | 
				
			||||||
 | 
					      filePicked = tempFiles?.[0]
 | 
				
			||||||
      filePath = tempFilePaths?.[0] // 在 H5 上并不是文件路径名,而是类似 "blob:http://localhost:8080/f0d3e54d-0694-4803-8097-641d76a10b0d“。// 在 iOS 上是 "_doc/uniapp_temp_1598593902955/compressed/1598593925815.png", 有时还包含从 file:/// 开始的完整路径名
 | 
					      filePath = tempFilePaths?.[0] // 在 H5 上并不是文件路径名,而是类似 "blob:http://localhost:8080/f0d3e54d-0694-4803-8097-641d76a10b0d“。// 在 iOS 上是 "_doc/uniapp_temp_1598593902955/compressed/1598593925815.png", 有时还包含从 file:/// 开始的完整路径名
 | 
				
			||||||
      let random = crypto.randomBytes(16).toString('hex')
 | 
					      let random = crypto.randomBytes(16).toString('hex')
 | 
				
			||||||
      // #ifndef WEB
 | 
					      // #ifndef WEB
 | 
				
			||||||
@ -392,13 +408,13 @@ module.exports = {
 | 
				
			|||||||
      cloudPath = `WEB_${wo.envar.clientInfo.osName}_${random}${path.extname(tempFiles?.[0]?.name || '')}` // name is available in H5 only. 只包含文件名和后缀名,不包含路径。
 | 
					      cloudPath = `WEB_${wo.envar.clientInfo.osName}_${random}${path.extname(tempFiles?.[0]?.name || '')}` // name is available in H5 only. 只包含文件名和后缀名,不包含路径。
 | 
				
			||||||
      // #endif
 | 
					      // #endif
 | 
				
			||||||
      if (!path.extname(cloudPath)) cloudPath += '.jpg'
 | 
					      if (!path.extname(cloudPath)) cloudPath += '.jpg'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    } else if (mediaType === 'video') {
 | 
					    } else if (mediaType === 'video') {
 | 
				
			||||||
      let [errorChoose, { tempFilePath, tempFile, duration, size, width, height, name }] = await uni.chooseVideo({ sourceType, maxDuration })
 | 
					      let [errorChoose, { tempFilePath, tempFile, duration, size, width, height, name }] = await uni.chooseVideo({ sourceType, maxDuration })
 | 
				
			||||||
      if (errorChoose) {
 | 
					      if (errorChoose) {
 | 
				
			||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = size
 | 
					      fileSize = size
 | 
				
			||||||
 | 
					      filePicked = tempFile
 | 
				
			||||||
      filePath = tempFilePath // 在 iOS 上形如 "file:///var/mobile/Containers/Data/Application/55A76332-44F5-4D5F-A9F6-3F857D584883/Documents/Pandora/apps/26B43CD2F587D37FC6799108434A6F84/doc/uniapp_temp_1598596171580/gallery/IMG_3082.MOV"
 | 
					      filePath = tempFilePath // 在 iOS 上形如 "file:///var/mobile/Containers/Data/Application/55A76332-44F5-4D5F-A9F6-3F857D584883/Documents/Pandora/apps/26B43CD2F587D37FC6799108434A6F84/doc/uniapp_temp_1598596171580/gallery/IMG_3082.MOV"
 | 
				
			||||||
      let random = crypto.randomBytes(16).toString('hex')
 | 
					      let random = crypto.randomBytes(16).toString('hex')
 | 
				
			||||||
      // #ifndef WEB
 | 
					      // #ifndef WEB
 | 
				
			||||||
@ -425,11 +441,16 @@ module.exports = {
 | 
				
			|||||||
        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '文件选择失败。请稍后再试,或向客服投诉。', enUS: 'File choose failed. Please try again later, or report to customer service.' } }
 | 
					        return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '文件选择失败。请稍后再试,或向客服投诉。', enUS: 'File choose failed. Please try again later, or report to customer service.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      fileSize = tempFiles?.[0]?.size
 | 
					      fileSize = tempFiles?.[0]?.size
 | 
				
			||||||
      if (fileSize) {
 | 
					      filePicked = tempFiles?.[0]
 | 
				
			||||||
        return { _state: 'SUCCESS', fileUrl: tempFiles?.[0]?.url }
 | 
					
 | 
				
			||||||
      } else {
 | 
					      let fileExt = filePicked?.name?.split?.('.')?.pop?.()?.toLowerCase?.()
 | 
				
			||||||
 | 
					      if (Array.isArray(mediaType) && !mediaType?.includes?.('.' + fileExt)) {
 | 
				
			||||||
 | 
					        return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '不支持的文件:\n' + filePicked?.name, enUS: 'Unsupported file:\n' + filePicked?.name } }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!fileSize) {
 | 
				
			||||||
        return { _state: 'CER_EMPTY_FILE', _msg: { zhCN: '文件为空,无法上传。', enUS: 'Empty files cannot be uploaded.' } }
 | 
					        return { _state: 'CER_EMPTY_FILE', _msg: { zhCN: '文件为空,无法上传。', enUS: 'Empty files cannot be uploaded.' } }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      return { _state: 'SUCCESS', fileUrl: tempFiles?.[0]?.url }
 | 
				
			||||||
      // #endif
 | 
					      // #endif
 | 
				
			||||||
      // #ifndef WEB
 | 
					      // #ifndef WEB
 | 
				
			||||||
      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '请切换到网页端上传文件!', enUS: 'Please switch to WebApp to upload files.' } }
 | 
					      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '请切换到网页端上传文件!', enUS: 'Please switch to WebApp to upload files.' } }
 | 
				
			||||||
@ -443,6 +464,13 @@ module.exports = {
 | 
				
			|||||||
      return { _state: 'CER_FILE_TOO_LARGE', _msg: { zhCN: `文件太大了,无法上传。最大允许 ${sizeLimitMB}`, enUS: `File too large to upload. Maximum allowed is ${sizeLimitMB}` } }
 | 
					      return { _state: 'CER_FILE_TOO_LARGE', _msg: { zhCN: `文件太大了,无法上传。最大允许 ${sizeLimitMB}`, enUS: `File too large to upload. Maximum allowed is ${sizeLimitMB}` } }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let fileExt = filePicked?.name?.split?.('.')?.pop?.()?.toLowerCase?.()
 | 
				
			||||||
 | 
					    if (mediaType === 'image' && !my.isImageFile(fileExt)
 | 
				
			||||||
 | 
					      || mediaType === 'video' && !my.isVideoFile(fileExt)
 | 
				
			||||||
 | 
					      || Array.isArray(mediaType) && !mediaType?.includes?.('.' + fileExt)) {
 | 
				
			||||||
 | 
					      return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '不支持的文件:\n' + filePicked?.name, enUS: 'Unsupported file:\n' + filePicked?.name } }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (filePath) {
 | 
					    if (filePath) {
 | 
				
			||||||
      this.showLoading()
 | 
					      this.showLoading()
 | 
				
			||||||
      const { fileID, requestId } = await uniCloud.uploadFile({
 | 
					      const { fileID, requestId } = await uniCloud.uploadFile({
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user