1、UploadController
'use strict';
const Controller = require('egg').Controller;
class UploadController extends Controller {
async index() {
const {ctx} = this;
ctx.body = await ctx.service.upload.upload();
}
}
module.exports = UploadController;
2、UploadService
'use strict';
const Service = require('egg').Service;
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
class UploadService extends Service {
/**
* 上传入口
* @returns {Promise.<*>}
*/
async upload() {
const {ctx} = this;
// 获取 steam
const stream = await ctx.getFileStream();
/**
* 文件路径
*/
const {uplaodBasePath, filename, filePath, name} = this.setFlieByStream(stream);
/**
* 检验md5
*/
const {status, flieBuffer, data} = await this.checkMd5(stream, {filePath, name});
/**
* 如果找到md5 则直接将信息返回 不再重新生成资源
*/
if (status === 'found') {
return this.createResponse(stream, data.path);
} else {
/**
* 生成文件目录
*/
const {targetPath} = this.createUploadPath({uplaodBasePath, filename});
/**
* 图片信息写入
*/
await this.writeFile({path: targetPath, file: flieBuffer});
/**
* 数据插入数据库
*/
await this.createMD5(data);
return this.createResponse(stream, data.path);
}
}
/**
* 文件写入
* @param path
* @param file
* @returns {Promise}
*/
async writeFile({path, file}) {
return new Promise((resolve, reject) => {
fs.writeFile(path, file, function (err) {
if (err) reject(err);
resolve('successed')
});
})
}
/**
* 创建响应
* @param stream
* @param filePath
* @returns {*}
*/
createResponse(stream, filePath) {
return {path: filePath};
}
/**
* 检验md5
* @param stream
* @param fileInfo
* @returns {Promise}
*/
async checkMd5(stream, fileInfo) {
const {ctx} = this;
const {filePath, name} = fileInfo;
/**
* 生成md5 然后先查询根据md5查询数据库
* 如果数据库存在 将值拿到直接返回 不在创建文件
* 如果值不存在 则创建文件后 再把路径返回
*/
return new Promise((resolve, reject) => {
const fsHash = crypto.createHash('md5');
/**
* 保存流数据 后续用于写入
*/
let flieBuffer;
stream.on('data', function (d) {
flieBuffer ? flieBuffer = Buffer.concat([flieBuffer, d]) : flieBuffer = d;
fsHash.update(d);
});
stream.on('end', function () {
const md5 = fsHash.digest('hex');
// 根据md5查询 是否存在
ctx.connector.upload.fetchByMd5(md5).then(res => {
// 存在该资源 编辑状态为found 然后返回
if (res.dataValues) {
resolve({
data: res.dataValues,
status: 'found'
})
// 不存在 则在数据库中创建该条资源 并将资源写入 标记状态为create
} else {
resolve({
status: 'create',
flieBuffer: flieBuffer,
data: {
md5,
name,
path: filePath
}
})
}
})
});
});
}
/**
* 生成md5
*/
async createMD5(data) {
const {ctx} = this;
/**
* 生成md5 然后先查询根据md5查询数据库
* 如果数据库存在 将值拿到直接返回 不在创建文件
* 如果值不存在 则创建文件后 再把路径返回
*/
return new Promise((resolve, reject) => {
ctx.connector.upload.createMd5(data).then(res => {
resolve({
status: data
})
});
});
}
/***
* 创建最终的文件夹 及文件名称
* @param filename
* @param uplaodBasePath
* @returns {{target: string}}
*/
createUploadPath({filename, uplaodBasePath}) {
// 没有文件则创建文件目录
const filePath = path.join(this.config.baseDir, uplaodBasePath);
if (!fs.existsSync(filePath)) fs.mkdirSync(filePath);
// 生成写入路径
const targetPath = path.join(this.config.baseDir, uplaodBasePath, filename);
return {
targetPath
}
}
/**
* 设置文件路径和文件名称
* @param stream
* @returns {{uplaodBasePath: string, filename: string}}
*/
setFlieByStream(stream) {
// 额外的路径
const _path = stream.fields.path || '';
const actionPath = _path ? _path + '/' : 'upload/';
// 上传基础目录
const uplaodBasePath = ('upload/' + actionPath).replace(/\/\//g, '/');
// 生成文件名
const filename = Date.now() + '_' + stream.filename;
// 返回文件路径
const filePath = `/${uplaodBasePath}${filename}`.replace(/\\/g, '/');
return {
uplaodBasePath,
filename,
filePath,
name: stream.filename
}
}
}
module.exports = UploadService;
3、ant
<Upload name="logo"
{...upload}
onChange={uploadOnChange}
method="POST"
multiple={false}
action="/upload"
data={UploadData}
accept=".png,.jpg,.jpeg,.svg"
listType="picture">
{
hasFileList ? '' :
<Button>
<UploadOutlined/> 请选择背景图片
</Button>
}
</Upload>