'use strict'; const Controller = require('egg').Controller; const moment = require('moment'); const { sep, extname } = require('path'); const fs = require('fs'); const awaitWriteStream = require('await-stream-ready').write; const sendToWormhole = require('stream-wormhole'); const assert = require('assert'); const wxstream = require('stream'); const ffmpeg = require('fluent-ffmpeg'); const { BusinessError, ErrorCode } = require('naf-core').Error; class FilesController extends Controller { async upload() { const { ctx, app } = this; const { appid, catalog, item } = ctx.params; assert(appid); const stream = await ctx.getFileStream(); ctx.logger.debug(stream); const rootPath = `${app.config.cdn.repos_root_path}`; const rootUrl = `${app.config.cdn.repos_root_url}`; const dirs = [ appid ]; if (catalog && catalog !== '_') { const subs = catalog.split('_'); dirs.push(...subs); } const saved = await this.saveFile(rootPath, dirs, stream, item); const uri = `${rootUrl}/${dirs.join('/')}/${saved.fileName}`; ctx.body = { errcode: 0, errmsg: 'ok', id: saved.id, name: saved.fileName, uri }; } async saveFile(rootPath, dirs, stream, name) { const ctx = this.ctx; const ext = extname(stream.filename).toLowerCase(); // TODO: 指定文件名或者按时间生成文件名 // const name = moment().format('YYYYMMDDHHmmss'); if (!name) name = moment().format('YYYYMMDDHHmmss'); // TODO: 检查根路径是否存在,不存在则创建 ctx.logger.debug('rootPath: ', rootPath); if (!fs.existsSync(rootPath)) { ctx.logger.debug('create dir: ', rootPath); fs.mkdirSync(rootPath); } // TODO: 检查分级目录是否存在,不存在则创建 for (let i = 0; i < dirs.length; i++) { const p = `${rootPath}${sep}${dirs.slice(0, i + 1).join(sep)}`; if (!fs.existsSync(p)) { fs.mkdirSync(p); } } const filePath = `${rootPath}${sep}${dirs.join(sep)}${sep}`; const fileName = `${name}${ext}`; const writeStream = fs.createWriteStream(filePath + fileName); try { await awaitWriteStream(stream.pipe(writeStream)); } catch (err) { await sendToWormhole(stream); throw err; } return { filePath, fileName, id: name }; } async wxupload() { const { serverid, type, appid } = this.ctx.request.body; assert(serverid, 'serverid不允许为空'); assert(type, 'type不允许为空'); const { ctx, app } = this; const url = app.config.wx.wxdown(appid, serverid); const file = await this.ctx.curl(url); const stream = new wxstream.PassThrough(); stream.end(file.data); const rootPath = `${app.config.cdn.repos_root_path}`; const rootUrl = `${app.config.cdn.repos_root_url}`; let uri; // 判断类型 0、图片 1、音频 2、视频 if (type === '0') { const dirs = [ 'wximage' ]; const saved = await this.wxsaveFile(rootPath, dirs, stream, '.jpg'); uri = `${rootUrl}/${dirs.join('/')}/${saved.fileName}`; } else if (type === '1') { const dirs = [ 'wxadio' ]; const saved = await this.wxsaveFile(rootPath, dirs, stream, '.amr'); // 音频时如果需要使用转码,以下调用ffmpeg进行转码处理 // const hz = '.mp3'; const resultname = `${saved.id}.amr`; // const ffurl = `${saved.filePath}${resultname}`; // if (!ffmpeg) throw new BusinessError(ErrorCode.SERVICE_FAULT, '转换失败'); // new ffmpeg({ source: `${saved.filePath}${saved.fileName}` }) // .saveToFile(ffurl, function(retcode, error) { // }) // .on('end', function() { // // 在这里处理完成后的结果 // console.log('Finished processing'); // }); uri = `${rootUrl}/${dirs.join('/')}/${resultname}`; } else if (type === '2') { const dirs = [ 'wxvideo' ]; const saved = await this.wxsaveFile(rootPath, dirs, stream, ''); uri = `${rootUrl}/${dirs.join('/')}/${saved.fileName}`; } ctx.body = { errcode: 0, errmsg: 'ok', uri }; } async wxsaveFile(rootPath, dirs, stream, ext) { const ctx = this.ctx; // TODO: 指定文件名或者按时间生成文件名 const name = moment().format('YYYYMMDDHHmmss'); // TODO: 检查根路径是否存在,不存在则创建 ctx.logger.debug('rootPath: ', rootPath); if (!fs.existsSync(rootPath)) { ctx.logger.debug('create dir: ', rootPath); fs.mkdirSync(rootPath); } // TODO: 检查分级目录是否存在,不存在则创建 for (let i = 0; i < dirs.length; i++) { const p = `${rootPath}${sep}${dirs.slice(0, i + 1).join(sep)}`; if (!fs.existsSync(p)) { fs.mkdirSync(p); } } const filePath = `${rootPath}${sep}${dirs.join(sep)}${sep}`; const fileName = `${name}${ext}`; const writeStream = fs.createWriteStream(filePath + fileName); try { await awaitWriteStream(stream.pipe(writeStream)); } catch (err) { await sendToWormhole(stream); throw err; } return { filePath, fileName, id: name }; } async serverUpload() { const { ctx, app } = this; const body = ctx.request.body; const { name, uri, extension, code } = body; const path = uri; if (!path) { return this.getErrorReturn('缺少文件存储路径'); } const rootPath = `${app.config.cdn.repos_root_path}`; const rootUrl = `${app.config.cdn.repos_root_url}`; if (!fs.existsSync(rootPath)) { ctx.logger.debug('create dir: ', rootPath); fs.mkdirSync(rootPath); } const dirs = path.split('/'); dirs.pop(); // 检查路径是否存在,不存在就创建 let realPath = rootPath; const realPathArray = []; for (const p of dirs) { const children = p.split('_'); for (const c of children) { realPath = `${realPath}${sep}${c}`; if (!fs.existsSync(realPath)) { fs.mkdirSync(realPath); } realPathArray.push(c); } } const filePath = `${rootUrl}/${realPathArray.join('/')}`; const fileName = `${name || new Date().getTime()}.${extension}`; try { const base64Data = code.replace(/^data:image\/\w+;base64,/, ''); const dataBuffer = Buffer(base64Data, 'base64'); await fs.writeFileSync(`${realPath}${sep}${fileName}`, Buffer(dataBuffer, 'base64')); } catch (error) { throw error; } const returnUri = `${filePath}/${fileName}`; this.ctx.body = this.getSuccessReturn({ uri: returnUri, id: fileName }); } getErrorReturn(message) { return { errcode: -1, errmsg: message || '程序发生错误' }; } getSuccessReturn(data) { return { errcode: 0, errmsg: 'ok', data }; } } module.exports = FilesController;