Formidable: Better handling for file uploads. Now supports multiple keys
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
This commit is contained in:
parent
baf2d896c1
commit
5f916e97ea
5 changed files with 101 additions and 44 deletions
46
flaska.mjs
46
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
BIN
test/test.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Loading…
Reference in a new issue