- Notifications
You must be signed in to change notification settings - Fork36
eggjs/multipart
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Useco-busboy to upload file by streaming andprocess it without save to disk(using thestream mode).
Just usectx.multipart() to got file stream, then pass to image processing module such asgm or upload to cloud storage such asoss.
For security, if uploading file extension is not in white list, will response as400 Bad request.
Default Whitelist:
constwhitelist=[// images'.jpg','.jpeg',// image/jpeg'.png',// image/png, image/x-png'.gif',// image/gif'.bmp',// image/bmp'.wbmp',// image/vnd.wap.wbmp'.webp','.tif','.psd',// text'.svg','.js','.jsx','.json','.css','.less','.html','.htm','.xml',// tar'.zip','.gz','.tgz','.gzip',// video'.mp3','.mp4','.avi',];
The default fileSize that multipart can accept is10mb. if you upload a large file, you should specify this config.
// config/config.default.jsexports.multipart={fileSize:'50mb',};
Developer can custom additional file extensions:
// config/config.default.jsexports.multipart={// will append to whilelistfileExtensions:['.foo','.apk',],};
Can alsooverride built-in whitelist, such as only allow png:
// config/config.default.jsexports.multipart={whitelist:['.png',],};
Or by function:
exports.multipart={whitelist:(filename)=>['.png'].includes(path.extname(filename)||'')};
Note: if definewhitelist, thenfileExtensions will be ignored.
More examples please follow:
If you don't know theNode.js Stream work,maybe you should use thefile mode to get started.
The usage very similar tobodyParser.
ctx.request.body: Get all the multipart fields and values, exceptfile.ctx.request.files: Contains allfilefrom the multipart request, it's an Array object.
WARNING: you should remove the temporary upload files after you use it,theasync ctx.cleanupRequestFiles() method will be very helpful.
You need to setconfig.multipart.mode = 'file' to enablefile mode:
// config/config.default.jsexports.multipart={mode:'file',};
Afterfile mode enable, egg will remove the old temporary files(don't include today's files) on04:30 AM every day by default.
config.multipart={mode:'file',tmpdir:path.join(os.tmpdir(),'egg-multipart-tmp',appInfo.name),cleanSchedule:{// run tmpdir clean job on every day 04:30 am// cron style see https://github.com/eggjs/egg-schedule#cron-style-schedulingcron:'0 30 4 * * *',disable:false,},};
Default will use the last field which has same name, if need the all fields value, please setallowArrayField in config.
// config/config.default.jsexports.multipart={mode:'file',allowArrayField:true,};
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"> title:<inputname="title"/> file:<inputname="file"type="file"/><buttontype="submit">Upload</button></form>
Controller which hanlderPOST /upload:
// app/controller/upload.jsconstController=require('egg').Controller;constfs=require('mz/fs');module.exports=classextendsController{asyncupload(){const{ ctx}=this;constfile=ctx.request.files[0];constname='egg-multipart-test/'+path.basename(file.filename);letresult;try{// process file or upload to cloud storageresult=awaitctx.oss.put(name,file.filepath);}finally{// remove tmp files and don't block the request's response// cleanupRequestFiles won't throw error even remove file io error happenctx.cleanupRequestFiles();// remove tmp files before send response// await ctx.cleanupRequestFiles();}ctx.body={url:result.url,// get all field valuesrequestBody:ctx.request.body,};}};
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"> title:<inputname="title"/> file1:<inputname="file1"type="file"/> file2:<inputname="file2"type="file"/><buttontype="submit">Upload</button></form>
Controller which hanlderPOST /upload:
// app/controller/upload.jsconstController=require('egg').Controller;constfs=require('mz/fs');module.exports=classextendsController{asyncupload(){const{ ctx}=this;console.log(ctx.request.body);console.log('got %d files',ctx.request.files.length);for(constfileofctx.request.files){console.log('field: '+file.fieldname);console.log('filename: '+file.filename);console.log('encoding: '+file.encoding);console.log('mime: '+file.mime);console.log('tmp filepath: '+file.filepath);letresult;try{// process file or upload to cloud storageresult=awaitctx.oss.put('egg-multipart-test/'+file.filename,file.filepath);}finally{// remove tmp files and don't block the request's response// cleanupRequestFiles won't throw error even remove file io error happenctx.cleanupRequestFiles([file]);}console.log(result);}}};
If you're well-known about know the Node.js Stream work, you should use thestream mode.
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"> title:<inputname="title"/> file1:<inputname="file1"type="file"/> file2:<inputname="file2"type="file"/><buttontype="submit">Upload</button></form>
Controller which hanlderPOST /upload:
// app/controller/upload.jsconst{ Controller}=require('egg');constfs=require('fs');conststream=require('stream');constutil=require('util');const{ randomUUID}=require('crypto');constpipeline=util.promisify(stream.pipeline);module.exports=classUploadControllerextendsController{asyncupload(){constparts=this.ctx.multipart();constfields={};constfiles={};forawait(constpartofparts){if(Array.isArray(part)){// fieldsconsole.log('field: '+part[0]);console.log('value: '+part[1]);}else{// otherwise, it's a streamconst{ filename, fieldname, encoding, mime}=part;console.log('field: '+fieldname);console.log('filename: '+filename);console.log('encoding: '+encoding);console.log('mime: '+mime);// how to handler?// 1. save to tmpdir with pipeline// 2. or send to oss// 3. or just consume it with another for await// WARNING: You should almost never use the origin filename as it could contain malicious input.consttargetPath=path.join(os.tmpdir(),randomUUID()+path.extname(filename));awaitpipeline(part,createWriteStream(targetPath));// use `pipeline` not `pipe`}}this.ctx.body='ok';}};
You can got upload stream byctx.getFileStream*().
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"> title:<inputname="title"/> file:<inputname="file"type="file"/><buttontype="submit">Upload</button></form>
Controller which handlerPOST /upload:
// app/controller/upload.jsconstpath=require('node:path');const{ sendToWormhole}=require('stream-wormhole');const{ Controller}=require('egg');module.exports=classextendsController{asyncupload(){const{ ctx}=this;// file not exists will response 400 errorconststream=awaitctx.getFileStream();constname='egg-multipart-test/'+path.basename(stream.filename);// process file or upload to cloud storageconstresult=awaitctx.oss.put(name,stream);ctx.body={url:result.url,// process form fields by `stream.fields`fields:stream.fields,};}asyncuploadNotRequiredFile(){const{ ctx}=this;// file not requiredconststream=awaitctx.getFileStream({requireFile:false});letresult;if(stream.filename){constname='egg-multipart-test/'+path.basename(stream.filename);// process file or upload to cloud storageconstresult=awaitctx.oss.put(name,stream);}else{// must consume the empty streamawaitsendToWormhole(stream);}ctx.body={url:result&&result.url,// process form fields by `stream.fields`fields:stream.fields,};}};
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"> title:<inputname="title"/> file1:<inputname="file1"type="file"/> file2:<inputname="file2"type="file"/><buttontype="submit">Upload</button></form>
Controller which hanlderPOST /upload:
// app/controller/upload.jsconstController=require('egg').Controller;module.exports=classextendsController{asyncupload(){const{ ctx}=this;constparts=ctx.multipart();letpart;while((part=awaitparts())!=null){if(part.length){// arrays are busboy fieldsconsole.log('field: '+part[0]);console.log('value: '+part[1]);console.log('valueTruncated: '+part[2]);console.log('fieldnameTruncated: '+part[3]);}else{if(!part.filename){// user click `upload` before choose a file,// `part` will be file stream, but `part.filename` is empty// must handler this, such as log error.continue;}// otherwise, it's a streamconsole.log('field: '+part.fieldname);console.log('filename: '+part.filename);console.log('encoding: '+part.encoding);console.log('mime: '+part.mime);constresult=awaitctx.oss.put('egg-multipart-test/'+part.filename,part);console.log(result);}}console.log('and we are done parsing the form!');}};
If the defaultmode isstream, use thefileModeMatch options to match the request urls switch tofile mode.
config.multipart={mode:'stream',// let POST /upload_file request use the file mode, other requests use the stream mode.fileModeMatch:/^\/upload_file$/,// or glob// fileModeMatch: '/upload_file',};
NOTICE:fileModeMatch options only work onstream mode.
Made withcontributors-img.
About
multipart plugin for egg
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.