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 filename let keys = Object.keys(files).filter(key => Boolean(ctx.req.files[key]))
let target
try { Promise.all(
filename = opts.filename(ctx.req.file) || ctx.req.file.name keys.map(key => {
target = path.join(opts.uploadDir, filename) let filename
} catch (err) { let target
return rej(err)
} try {
filename = opts.filename(ctx.req.files[key]) || ctx.req.files[key].name
rename(ctx.req.file.path, target) target = path.join(opts.uploadDir, filename)
.then(function() { } catch (err) {
ctx.req.file.path = target return Promise.reject(err)
ctx.req.file.filename = filename }
})
.then(res, rej)
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)
}) })
}) })
} }

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,34 +126,51 @@ 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 boundary = `---------${random(32)}`
const crlf = '\r\n' 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 filename = path.basename(file)
const boundary = `---------${random(32)}`
const multipartBody = Buffer.concat([ uploadBody.push(Buffer.from(
Buffer.from( `${crlf}--${boundary}${crlf}` +
`${crlf}--${boundary}${crlf}` + `Content-Disposition: form-data; name="${key}"; filename="${filename}"` + crlf + crlf
`Content-Disposition: form-data; name="file"; filename="${filename}"` + crlf + crlf ))
), uploadBody.push(data)
data, })
Buffer.concat(Object.keys(body).map(function(key) { }))
return Buffer.from('' .then(() => {
+ `${crlf}--${boundary}${crlf}` uploadBody.push(
+ `Content-Disposition: form-data; name="${key}"` + crlf + crlf Buffer.concat(Object.keys(body).map(function(key) {
+ JSON.stringify(body[key]) return Buffer.from(''
) + `${crlf}--${boundary}${crlf}`
})), + `Content-Disposition: form-data; name="${key}"` + crlf + crlf
Buffer.from(`${crlf}--${boundary}--`), + JSON.stringify(body[key])
]) )
}))
)
uploadBody.push(Buffer.from(`${crlf}--${boundary}--`))
return this.customRequest(method, url, multipartBody, { let multipartBody = Buffer.concat(uploadBody)
timeout: 5000,
headers: { return this.customRequest(method, url, multipartBody, {
'Content-Type': 'multipart/form-data; boundary=' + boundary, timeout: 5000,
'Content-Length': multipartBody.length, headers: {
}, 'Content-Type': 'multipart/form-data; boundary=' + boundary,
}) 'Content-Length': multipartBody.length,
},
})
}) })
} }

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