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.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)
})
})
}

View file

@ -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": {

View file

@ -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,
},
})
})
}

View file

@ -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,

BIN
test/test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB