test uploaded file types by file extension

This commit is contained in:
Luk 2024-08-08 11:30:29 +08:00
parent bc00d6f295
commit 22d79ffc06

View File

@ -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
const BASE_TYPE_DEFAULT = 'SERVER' // one of { SERVER: 服务器, UNICLOUD_FUNC: 云函数, UNICLOUD_OBJECT: 云对象 }
@ -7,11 +10,17 @@ const my = {
// getCurrentPages() 在 topWindow/App.vue 里有可能为空,所以用 getApp().$store 更安全. 20230513: 发现在微信小程序模拟器里getApp().$store.state 未定义,所以还是用 globalThis.wo?.ss
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 = {
// 用直观的色彩,代替语义化的类型:
// uView 中大量组件都使用 type 来设置颜色,而 uni-ui 中只有少量组件用到。
@ -293,20 +302,22 @@ module.exports = {
} = {}) {
// 有的管理后台不需要登录就允许上传,例如 cmctoy。因此不要在这里依赖登录状态。
let filePath, fileSize
let filePath, fileSize, filePicked
if (mediaType === 'image') {
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
if (errorChoose) {
return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
}
fileSize = tempFiles?.[0]?.size
filePicked = tempFiles?.[0]
filePath = tempFilePaths?.[0]
} 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) {
return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
}
fileSize = size
filePicked = tempFile
filePath = tempFilePath
} else {
// #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.' } }
}
fileSize = tempFiles?.[0]?.size
filePicked = tempFiles?.[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
// #ifndef WEB
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}` } }
}
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) {
for (let key in formData) {
// multer 不会自动处理 JSON 数据,必须前后端配合处理
@ -374,13 +389,14 @@ module.exports = {
// return { _state: 'USER_OFFLINE', errMsg: 'offline user cannot upload files' }
// }
let filePath, cloudPath, fileSize
let filePath, cloudPath, fileSize, filePicked
if (mediaType === 'image') {
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
if (errorChoose) {
return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '图像选择失败。请稍后再试,或向客服投诉。', enUS: 'Image choose failed. Please try again later, or report to customer service.' } }
}
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:/// 开始的完整路径名
let random = crypto.randomBytes(16).toString('hex')
// #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. 只包含文件名和后缀名,不包含路径。
// #endif
if (!path.extname(cloudPath)) cloudPath += '.jpg'
} else if (mediaType === 'video') {
let [errorChoose, { tempFilePath, tempFile, duration, size, width, height, name }] = await uni.chooseVideo({ sourceType, maxDuration })
if (errorChoose) {
return { _state: 'CER_FAIL_CHOOSE', _msg: { zhCN: '视频选择失败。请稍后再试,或向客服投诉。', enUS: 'Video choose failed. Please try again later, or report to customer service.' } }
}
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"
let random = crypto.randomBytes(16).toString('hex')
// #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.' } }
}
fileSize = tempFiles?.[0]?.size
if (fileSize) {
return { _state: 'SUCCESS', fileUrl: tempFiles?.[0]?.url }
} else {
filePicked = tempFiles?.[0]
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: 'SUCCESS', fileUrl: tempFiles?.[0]?.url }
// #endif
// #ifndef WEB
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}` } }
}
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) {
this.showLoading()
const { fileID, requestId } = await uniCloud.uploadFile({