// 文件上传 const fs = require('fs') const config = require('../config') /** * 文件上传 * post: /upload */ module.exports = async ctx => { const body = ctx.request.body // 存储路径 const saveDir = config.upload // 文件名不含扩展名 const fn = body.fn // 文件路径不含斜杠 const fp = body.fp const files = ctx.request.files if (files) { // 文件(客户端表单名为:file) const file = files.file // 文件原来路径 const oldPath = file.path // 文件名称(当前含扩展名,后面不再含扩展名) let name = file.name const dot = name.lastIndexOf('.') // 文件扩展名(带.) const ext = name.substring(dot).toLowerCase() if (dot < 0) { ctx.body = { code: 2, message: '文件无扩展名' } } else if (fn && /;|\.|#|\\|\/|,/.test(fn)) { ctx.body = { code: 4, message: '文件名参数含非法字符' } } else if (fp && /;|\.|#|\\|\/|,/.test(fp)) { ctx.body = { code: 6, message: '文件路径参数含非法字符' } } else if ('.htm.html.js.php.asp.aspx'.includes(ext)) { ctx.body = { code: 5, message: '文件扩展名受限' } } else { let newPath = saveDir let url = '' // 已经指定名称(固定文件夹必须事先手工创建) if (fn) { if (fn.startsWith('crm_')) { url = 'crm/' + fn.substring(4) + '_' + Math.random().toString().slice(-8) } else if (fn.startsWith('face_')) { url = 'image/face/' + fn.substring(5) } else if ('.xls.xlsx'.includes(ext)) { url = 'excel/' + fn } else if ('.png.jpg.gif.jpeg.bmp.ico.svg'.includes(ext)) { url = 'image/' + fn } else if ('.pdf'.includes(ext)) { url = 'pdf/' + fn } else if ('.doc.docx'.includes(ext)) { url = 'word/' + fn } else if ('.txt'.includes(ext)) { url = 'txt/' + fn } else { url = 'other/' + fn } name = url } else { // 根据系统时间命名 const now = new Date() const time = { y: now.getFullYear(), m: ('0' + (now.getMonth() + 1)).slice(-2), d: ('0' + now.getDate()).slice(-2), hh: ('0' + now.getHours()).slice(-2), mm: ('0' + now.getMinutes()).slice(-2), ss: ('0' + now.getSeconds()).slice(-2) } // 创建文件夹 newPath = saveDir + '/' + time.y if (!fs.existsSync(newPath)) { fs.mkdirSync(newPath) } newPath = saveDir + '/' + time.y + '/' + time.m if (!fs.existsSync(newPath)) { fs.mkdirSync(newPath) } newPath = saveDir + '/' + time.y + '/' + time.m + '/' + time.d if (!fs.existsSync(newPath)) { fs.mkdirSync(newPath) } // 新文件名称(不含扩展名) name = time.hh + time.mm + time.ss + String(Math.random()).slice(-2) // 文件路径(不含主机名) url = time.y + '/' + time.m + '/' + time.d + '/' + name + ext } // 保存 try { const chunkTotal = body.totalChunks const data = fs.readFileSync(oldPath) fs.unlinkSync(oldPath) if (chunkTotal > 1) { // 分片上传 const chunkNumber = body.chunkNumber const totalSize = body.totalSize fs.writeFileSync(newPath + '/' + totalSize + '_' + chunkNumber + ext, data) // 最后一个时合并文件 if (chunkNumber === chunkTotal) { const wrStream = fs.createWriteStream(newPath + '/' + name + ext) const merge = i => { const chunkPath = newPath + '/' + totalSize + '_' + i + ext const reStream = fs.createReadStream(chunkPath) reStream.pipe(wrStream, { end: false }) reStream.once('end', () => { fs.unlinkSync(chunkPath) i < chunkTotal && merge(i + 1) }) } merge(1) } } else { // 单文件上传 fs.writeFileSync(newPath + '/' + name + ext, data) } ctx.body = { code: 0, data: url } } catch (err) { ctx.body = { code: 3, message: '文件转存出错' } } } } else { ctx.body = { code: 1, message: '没有检测到上传数据' } } }