diff --git a/flaska.mjs b/flaska.mjs index 0044556..9b60aea 100644 --- a/flaska.mjs +++ b/flaska.mjs @@ -248,29 +248,41 @@ export function FormidableHandler(formidable, org = {}) { } ctx.req.body = fields - ctx.req.file = files?.file || null + ctx.req.files = files + ctx.req.file = null - if (!ctx.req.file) { + + if (!ctx.req.files) { return res() } - let filename - let target + let keys = Object.keys(files).filter(key => Boolean(ctx.req.files[key])) - try { - filename = opts.filename(ctx.req.file) || ctx.req.file.name - target = path.join(opts.uploadDir, filename) - } catch (err) { - return rej(err) - } - - rename(ctx.req.file.path, target) - .then(function() { - ctx.req.file.path = target - ctx.req.file.filename = filename - }) - .then(res, rej) + Promise.all( + keys.map(key => { + let filename + let target + + try { + filename = opts.filename(ctx.req.files[key]) || ctx.req.files[key].name + target = path.join(opts.uploadDir, filename) + } catch (err) { + return Promise.reject(err) + } + return rename(ctx.req.files[key].path, target) + .then(function() { + ctx.req.files[key].path = target + ctx.req.files[key].filename = filename + }) + }) + ) + .then(() => { + if (keys.length === 1 && keys[0] === 'file') { + ctx.req.file = ctx.req.files.file + } + res() + }, rej) }) }) } diff --git a/package.json b/package.json index 957c47e..85ec795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flaska", - "version": "1.2.5", + "version": "1.3.0", "description": "Flaska is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.", "main": "flaska.mjs", "scripts": { diff --git a/test/client.mjs b/test/client.mjs index 5663c69..029c0b1 100644 --- a/test/client.mjs +++ b/test/client.mjs @@ -126,34 +126,51 @@ const random = (length = 8) => { return str; } -Client.prototype.upload = function(url, file, method = 'POST', body = {}) { - return fs.readFile(file).then(data => { - const crlf = '\r\n' +Client.prototype.upload = function(url, files, method = 'POST', body = {}) { + const boundary = `---------${random(32)}` + const crlf = '\r\n' + let upload = files + + if (typeof(upload) === 'string') { + upload = { + file: files + } + } + let keys = Object.keys(upload) + let uploadBody = [] + + return Promise.all(keys.map(key => { + let file = upload[key] + return fs.readFile(file).then(data => { const filename = path.basename(file) - const boundary = `---------${random(32)}` - const multipartBody = Buffer.concat([ - Buffer.from( - `${crlf}--${boundary}${crlf}` + - `Content-Disposition: form-data; name="file"; filename="${filename}"` + crlf + crlf - ), - data, - Buffer.concat(Object.keys(body).map(function(key) { - return Buffer.from('' - + `${crlf}--${boundary}${crlf}` - + `Content-Disposition: form-data; name="${key}"` + crlf + crlf - + JSON.stringify(body[key]) - ) - })), - Buffer.from(`${crlf}--${boundary}--`), - ]) + uploadBody.push(Buffer.from( + `${crlf}--${boundary}${crlf}` + + `Content-Disposition: form-data; name="${key}"; filename="${filename}"` + crlf + crlf + )) + uploadBody.push(data) + }) + })) + .then(() => { + uploadBody.push( + Buffer.concat(Object.keys(body).map(function(key) { + return Buffer.from('' + + `${crlf}--${boundary}${crlf}` + + `Content-Disposition: form-data; name="${key}"` + crlf + crlf + + JSON.stringify(body[key]) + ) + })) + ) + uploadBody.push(Buffer.from(`${crlf}--${boundary}--`)) - return this.customRequest(method, url, multipartBody, { - timeout: 5000, - headers: { - 'Content-Type': 'multipart/form-data; boundary=' + boundary, - 'Content-Length': multipartBody.length, - }, - }) + let multipartBody = Buffer.concat(uploadBody) + + return this.customRequest(method, url, multipartBody, { + timeout: 5000, + headers: { + 'Content-Type': 'multipart/form-data; boundary=' + boundary, + 'Content-Length': multipartBody.length, + }, + }) }) } diff --git a/test/http.test.mjs b/test/http.test.mjs index 168e31c..1d63c7b 100644 --- a/test/http.test.mjs +++ b/test/http.test.mjs @@ -60,6 +60,13 @@ flaska.post('/file/upload', FormidableHandler(formidable, { uploaded.push(ctx.req.file) ctx.body = ctx.req.file }) +flaska.post('/file/upload/many', FormidableHandler(formidable, { + uploadDir: './test/upload', +}), function(ctx) { + uploaded.push(ctx.req.files.herp) + uploaded.push(ctx.req.files.derp) + ctx.body = ctx.req.files +}) flaska.get('/file/leak', function(ctx) { file = fsSync.createReadStream('./test/test.png') ctx.body = file @@ -308,6 +315,27 @@ t.describe('/file/upload', function() { }) }) +t.describe('/file/upload/many', function() { + t.test('server should upload file', async function() { + let res = await client.upload('/file/upload/many', { + herp: './test/test.jpg', + derp: './test/test.png', + }) + + let [statSourcePng, statSourceJpg, statTargetHerp, statTargetDerp] = await Promise.all([ + fs.stat('./test/test.png'), + fs.stat('./test/test.jpg'), + fs.stat(res.herp.path), + fs.stat(res.derp.path), + ]) + + assert.strictEqual(statSourceJpg.size, statTargetHerp.size) + assert.strictEqual(statSourceJpg.size, res.herp.size) + assert.strictEqual(statSourcePng.size, statTargetDerp.size) + assert.strictEqual(statSourcePng.size, res.derp.size) + }) +}) + t.describe('HEAD', function() { const agent = new http.Agent({ keepAlive: true, diff --git a/test/test.jpg b/test/test.jpg new file mode 100644 index 0000000..f68a74f Binary files /dev/null and b/test/test.jpg differ