febs
Version:
febs is a useful utilities set
207 lines (185 loc) • 6.73 kB
JavaScript
;
/**
* Copyright (c) 2017 Copyright brainpoint All Rights Reserved.
* Author: lipengxiang
* Desc:
* upload控件使用一个接口来上传文件, 使用multpart/form-data方式传输:
* 1. uploadUrl: 上传文件.
* Example:
* 前台引入:
* 上传方式只能使用post.
* 1. 在需要upload的页面上引入 control_upload.hbs页面; 或者使用如下语句:
* <form id="fileForm" method="post" role="form" enctype="multipart/form-data">
* <input type="file" class="form-control" name="file" onchange="control_upload(cfg)" multiple>
* </form>
* 2. 调用脚本进行初始化设置: control_upload_init(uploadUrl, finishCB, progressCB);
* 其中 uploadUrl为上传地址, 不能带参数.
* 后台:
* 1. 在uploadUrl中调用 await require('febs').controls.upload.accept(app, conditionCB); 当满足条件时将存储, 并返回true表示成功.
*/
var URL = require('url');
var path = require('path');
var fs = require('fs');
var getRawBody = require('raw-body')
var assert = require('assert');
var febs = require('..');
var PromiseLib = Promise;
/**
* @desc: 在用户登出或其他中断传输中清除上传的数据.
* @param sessionGet: function() {} 用于获取存储在session中的临时文件信息;
* @param sessionClear: function() {} 用于清除存储在session中的临时信息
* @return:
*/
function cleanup(sessionGet, sessionClear, cleanFile = true) {
let uploadInfo = sessionGet();
if (uploadInfo) {
if (cleanFile && febs.file.fileIsExist(uploadInfo.filename))
febs.file.fileRemove(uploadInfo.filename);
sessionClear();
}
}
exports.cleanup = cleanup;
/**
* 准备接收上传文件.
* @param conditionCB: async function(data, filesize):string.
* - filesize: 将要存储的文件大小(base64大小)
* - data: 用户上传的数据.
* - return: 本地存储的文件路径, 返回null表示不存储. 存储的文件必须不存在.
* @param sessionSet: function(data){} 用于设置存储在session中的临时文件信息;
* @return Promise.
* @resolve
* - bool. 指明是否开始接收文件流.
*/
exports.acceptHeader = function(app, conditionCB, sessionSet)
{
assert(conditionCB);
return new PromiseLib((resolve, reject)=>{
try {
// ignore non-POST
if ('POST' != app.method) {
resolve(false);
return;
}
var body = app.request.body;
if (!body.chunks || !body.filesize) {
resolve(false);
return;
}
conditionCB(body.data, body.filesize)
.then(fn=>{
if (fn) {
if (febs.file.fileIsExist(fn)) {
console.debug('acceptHeader file is existed', __filename, __line);
reject('acceptHeader file is existed');
return;
}
sessionSet({ chunks:Number(body.chunks), filesize:Number(body.filesize), filename:fn, curChunk:0 });
app.response.body = {err:0};
resolve(true);
} else {
resolve(false);
}
})
.catch(e=>{
reject(e);
});
}
catch (e) {
reject(e);
}});
};
/**
* 上传文件内容.
* 发生错误会自动调用 cleanup
* @param finishCB: async function(filename):object.
* - filename: 本地存储的文件名.
* - return: 返回给客户端的数据. 不能包含err数据.
*
* @param sessionGet: function() {} 用于获取存储在session中的临时文件信息;
* @param sessionSet: function(data){} 用于设置存储在session中的临时文件信息;
* @param sessionClear: function() {} 用于清除存储在session中的临时信息
* @return Promise
* @resolve
* - data: 返回给客户端的数据.
*/
exports.accept = function(app, finishCB, sessionGet, sessionSet, sessionClear)
{
return new PromiseLib((resolve, reject)=>{
try {
// ignore non-POST
if ('POST' != app.method) {
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'method is not post'};
resolve(app.response.body);
return;
}
var url = URL.parse(app.request.url, true);
if (!url.query.crc32) {
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'leak param crc32'};
resolve(app.response.body);
return;
}
var sessionData = sessionGet();
if (!sessionData || !sessionData.chunks)
{
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'上传请求已经过期'};
resolve(app.response.body);
return;
}
getRawBody(app.req)
.then(bufstr=>{
if (febs.crypt.crc32(bufstr) != url.query.crc32)
{
app.response.body = {err:'crc32错误'};
resolve(app.response.body);
return;
}
bufstr = bufstr.toString('utf8');
// TODO: 匹配需优化.
if (sessionData.curChunk == 0) {
bufstr = bufstr.replace(/^data:image\/\w+;base64,/, '');
}
var base64Ret = febs.crypt.base64_decode(bufstr, sessionData.c1, sessionData.c2, sessionData.c3, sessionData.c4);
var buf = Buffer.from(base64Ret.data);
fs.appendFileSync(sessionData.filename, buf, {flag:'a+'});
sessionData.c1 = base64Ret.c1;
sessionData.c2 = base64Ret.c2;
sessionData.c3 = base64Ret.c3;
sessionData.c4 = base64Ret.c4;
sessionData.curChunk++;
sessionSet(sessionData);
if (sessionData.curChunk >= sessionData.chunks)
{
finishCB(sessionData.filename)
.then(fn=>{
cleanup(sessionGet, sessionClear, false);
if (fn.err !== 0)
fn.err = 0;
app.response.body = fn;
resolve(app.response.body);
})
.catch(err=>{
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'系统出错'};
reject(err);
});
return;
}
app.response.body = {err:0};
resolve(app.response.body);
})
.catch(err=>{
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'系统出错'};
reject(err);
});
}
catch(e) {
cleanup(sessionGet, sessionClear, true);
app.response.body = {err:'系统出错'};
reject(e);
}
});
};