Formidable: Better handling for file uploads. Now supports multiple keys
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded

This commit is contained in:
Jonatan Nilsson 2022-07-06 14:50:54 +00:00
parent baf2d896c1
commit 5f916e97ea
5 changed files with 101 additions and 44 deletions

View file

@ -248,29 +248,41 @@ export function FormidableHandler(formidable, org = {}) {
} }
ctx.req.body = fields 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() return res()
} }
let keys = Object.keys(files).filter(key => Boolean(ctx.req.files[key]))
Promise.all(
keys.map(key => {
let filename let filename
let target let target
try { try {
filename = opts.filename(ctx.req.file) || ctx.req.file.name filename = opts.filename(ctx.req.files[key]) || ctx.req.files[key].name
target = path.join(opts.uploadDir, filename) target = path.join(opts.uploadDir, filename)
} catch (err) { } catch (err) {
return rej(err) return Promise.reject(err)
} }
rename(ctx.req.file.path, target) return rename(ctx.req.files[key].path, target)
.then(function() { .then(function() {
ctx.req.file.path = target ctx.req.files[key].path = target
ctx.req.file.filename = filename ctx.req.files[key].filename = filename
}) })
.then(res, rej) })
)
.then(() => {
if (keys.length === 1 && keys[0] === 'file') {
ctx.req.file = ctx.req.files.file
}
res()
}, rej)
}) })
}) })
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "flaska", "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.", "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", "main": "flaska.mjs",
"scripts": { "scripts": {

View file

@ -126,27 +126,44 @@ const random = (length = 8) => {
return str; return str;
} }
Client.prototype.upload = function(url, file, method = 'POST', body = {}) { Client.prototype.upload = function(url, files, method = 'POST', body = {}) {
return fs.readFile(file).then(data => {
const crlf = '\r\n'
const filename = path.basename(file)
const boundary = `---------${random(32)}` const boundary = `---------${random(32)}`
const crlf = '\r\n'
let upload = files
const multipartBody = Buffer.concat([ if (typeof(upload) === 'string') {
Buffer.from( 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)
uploadBody.push(Buffer.from(
`${crlf}--${boundary}${crlf}` + `${crlf}--${boundary}${crlf}` +
`Content-Disposition: form-data; name="file"; filename="${filename}"` + crlf + crlf `Content-Disposition: form-data; name="${key}"; filename="${filename}"` + crlf + crlf
), ))
data, uploadBody.push(data)
})
}))
.then(() => {
uploadBody.push(
Buffer.concat(Object.keys(body).map(function(key) { Buffer.concat(Object.keys(body).map(function(key) {
return Buffer.from('' return Buffer.from(''
+ `${crlf}--${boundary}${crlf}` + `${crlf}--${boundary}${crlf}`
+ `Content-Disposition: form-data; name="${key}"` + crlf + crlf + `Content-Disposition: form-data; name="${key}"` + crlf + crlf
+ JSON.stringify(body[key]) + JSON.stringify(body[key])
) )
})), }))
Buffer.from(`${crlf}--${boundary}--`), )
]) uploadBody.push(Buffer.from(`${crlf}--${boundary}--`))
let multipartBody = Buffer.concat(uploadBody)
return this.customRequest(method, url, multipartBody, { return this.customRequest(method, url, multipartBody, {
timeout: 5000, timeout: 5000,

View file

@ -60,6 +60,13 @@ flaska.post('/file/upload', FormidableHandler(formidable, {
uploaded.push(ctx.req.file) uploaded.push(ctx.req.file)
ctx.body = 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) { flaska.get('/file/leak', function(ctx) {
file = fsSync.createReadStream('./test/test.png') file = fsSync.createReadStream('./test/test.png')
ctx.body = file 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() { t.describe('HEAD', function() {
const agent = new http.Agent({ const agent = new http.Agent({
keepAlive: true, keepAlive: true,

BIN
test/test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB