功能描述:
1. 用户将保存图片的文件夹进行压缩,通过网页上传;
2. 后端对接收到的压缩文件夹进行解压,并将图片转为web格式,存放至服务器指定位置;同时,把web图片列表的路径保存到image.json文件中;
3. 前端页面请求图片的路径时,后端则读取image.json文件的内容,将其返回,前端则只接收图片地址,显示出来即可。
实现过程:文件上传路径配置文件注:之所以将路径作为配置文件来写,是为了方便调试,例如,开发过程中服务器在本地,则解压之后的图片临时存放在本地,正式上线,图片路径则是服务器上指定的路径。
production.json
"upload": {
"host": "http://10.xxx.xxx.xx",
"tempPath": "src/__bak__/upload", // 临时地址
"outputPath": "/work/nginx/www/upload", // 解压、转码之后图片存放的地址
"accessFile": "/work/nginx/www/upload/images.json" // 保存图片路径的image.json文件的地址
}
testing.json
"upload": {
"host": "http://127.0.0.1",
"tempPath": "src/__bak__/upload",
"outputPath": "src/__bak__/images",
"accessFile": "src/__bak__/images/images.json"
}
后端共计三个接口
/**
* @api {GET} /upload 请求图片上传页面
* @apiDescription 请求图片上传页面
* @apiVersion 1.0.0
* @apiName uploadHTML
* @apiGroup upload
*
*/
router.get('/upload', upload_ctrl.getUploadHTML);
/**
* @api {POST} /upload 上传图片压缩文件
* @apiDescription 上传图片压缩文件
* @apiVersion 1.0.0
* @apiName uploadFile
* @apiGroup upload
*
* @apiParam (body) {files} path
*/
router.post('/upload', upload_ctrl.uploadFile);
/**
* @api {GET} /upload/img_urls 请求图片访问地址
* @apiDescription 请求图片访问地址
* @apiVersion 1.0.0
* @apiName getImgUrls
* @apiGroup upload
*
*/
router.get('/upload/img_urls', upload_ctrl.getImgUrls);
读取文件路径
const upload_conf = config.get<{ host: string; tempPath: string; outputPath: string; accessFile: string; }>('upload');
const url_conf = config.get<{ thumbor: { hostUrl: string; } }>('upstream');
第一步:用户访问上传页面 (http://127.0.0.1:8099/upload ),后端读取upload.html, 将其内容返回
async function getUploadHTML(ctx: RouterContext) {
const fileName = "./src/scm-board/router/file-upload/upload.html";
const res = fs.readFileSync(fileName);
ctx.res.setHeader('Content-Type', 'text/html;charset="utf-8');
ctx.body = res;
return;
}
第二步:用户选择压缩后的文件夹进行上传,后端将其保存到临时路径,然后读取,将文件夹解压、图片进行转码,并生成image.json,将转码后的图片路径放入image.json文件中
async function uploadFile(ctx: RouterContext) {
const files: Files = ctx.request.files;
if (!(files && files.image)) {
ctx.body = HoResponseUtils.error<any>({
error_no: 100301
});
return;
}
if (files.image && files.image['length']) {
ctx.body = HoResponseUtils.error<any>({
error_no: 100302
});
return;
}
ca_logger.info(upload_conf);
try {
// 另存为本地临时文件
const tmpPath = await _saveFile(files);
// 解压
const originPath = upload_conf.outputPath + '/origin';
const imgPathListStr = await _decompression(tmpPath, originPath);
// 转码
const thumbPath = upload_conf.outputPath + '/thumb';
await _thumbImgList(imgPathListStr, thumbPath);
} catch (error) {
ctx.body = HoResponseUtils.error<any>({
error_no: 100302,
error_message: error
});
return;
}
ctx.body = HoResponseUtils.normal<any>({
data: '上传成功'
});
return;
}
// 重命名为image,并另存为本地临时文件下
function _saveFile(files): Promise<string> {
const file = files.image as File;
const suffix = file.name.split('.').pop();
const tmpPath = `${upload_conf.tempPath}/images.${suffix}`;
return new Promise((resolve, reject) => {
try {
caUtils.mkdir(tmpPath);
const data = fs.readFileSync(file.path);
fs.writeFileSync(tmpPath, data)
resolve(tmpPath);
} catch (err) {
reject('文件读取失败,请上传正确的压缩文件夹!')
}
});
}
/**
* @param tempPath
* @param outputPath
* 读取 tempPath 路径下的压缩文件 解压之后,解压后的文件放至 outputPath
* 解压之后临时文件可删可不删,每次上传都会重命名为image,第二次上传,会覆盖掉第一次上传的压缩文件
*/
function _decompression(tempPath: string, outputPath: string): Promise<string> {
const outputArr = [];
const myStream = extractFull(tempPath, outputPath, {
$bin: pathTo7zip,
$progress: true
});
return new Promise((res, rej) => {
myStream.on('data', function (data) {
outputArr.push(upload_conf.host + '/upload/origin/' + data.file)
});
myStream.on('end', function () {
ca_logger.info('Complete!');
if (outputArr.length) {
res(outputArr.join(','));
} else {
rej('文件夹为空,请上传正确的压缩文件夹!');
}
});
myStream.on('error', (err) => {
ca_logger.error('Error:', err)
rej('文件解压失败,请上传正确的压缩文件夹!');
});
});
}
第三步:前端请求后端(http://127.0.0.1:8099/upload/img_urls),后端读取image.json文件并将其返回,前端则得到转码后的图片路径
async function getImgUrls(ctx: RouterContext) {
const filePath = upload_conf.accessFile;
if (!fs.existsSync(filePath)) {
ctx.body = HoResponseUtils.error<any>({
error_no: 100304
});
return;
}
try {
const res = fs.readFileSync(filePath);
const result = JSON.parse(res.toString())
ctx.body = HoResponseUtils.normal<any>({
data: result
});
return;
} catch (e) {
ctx.body = HoResponseUtils.error<any>({
error_no: 100305
});
return;
}
}
// 格式如下:
// {
// "code": 0,
// "message": null,
// "data": {
// "path":"/work/nginx/www/upload/11.jpg,/work/nginx/www/upload/12.jpg"
// }
// }
// 前端则将host+path即可显示图片
源码:https://github.com/zhanghongyublog/node.js/tree/master/file-upload