summaryrefslogtreecommitdiff
path: root/std/mime/multipart.ts
diff options
context:
space:
mode:
Diffstat (limited to 'std/mime/multipart.ts')
-rw-r--r--std/mime/multipart.ts66
1 files changed, 58 insertions, 8 deletions
diff --git a/std/mime/multipart.ts b/std/mime/multipart.ts
index 032d4e29d..c46948226 100644
--- a/std/mime/multipart.ts
+++ b/std/mime/multipart.ts
@@ -251,6 +251,17 @@ function skipLWSPChar(u: Uint8Array): Uint8Array {
return ret.slice(0, j);
}
+export interface MultipartFormData {
+ file(key: string): FormFile | undefined;
+ value(key: string): string | undefined;
+ entries(): IterableIterator<[string, string | FormFile | undefined]>;
+ [Symbol.iterator](): IterableIterator<
+ [string, string | FormFile | undefined]
+ >;
+ /** Remove all tempfiles */
+ removeAll(): Promise<void>;
+}
+
/** Reader for parsing multipart/form-data */
export class MultipartReader {
readonly newLine = encoder.encode("\r\n");
@@ -268,11 +279,11 @@ export class MultipartReader {
* overflowed file data will be written to temporal files.
* String field values are never written to files.
* null value means parsing or writing to file was failed in some reason.
+ * @param maxMemory maximum memory size to store file in memory. bytes. @default 1048576 (1MB)
* */
- async readForm(
- maxMemory: number
- ): Promise<{ [key: string]: null | string | FormFile }> {
- const result = Object.create(null);
+ async readForm(maxMemory = 10 << 20): Promise<MultipartFormData> {
+ const fileMap = new Map<string, FormFile>();
+ const valueMap = new Map<string, string>();
let maxValueBytes = maxMemory + (10 << 20);
const buf = new Buffer(new Uint8Array(maxValueBytes));
for (;;) {
@@ -292,11 +303,11 @@ export class MultipartReader {
throw new RangeError("message too large");
}
const value = buf.toString();
- result[p.formName] = value;
+ valueMap.set(p.formName, value);
continue;
}
// file
- let formFile: FormFile | null = null;
+ let formFile: FormFile | undefined;
const n = await copy(buf, p);
const contentType = p.headers.get("content-type");
assert(contentType != null, "content-type must be set");
@@ -333,9 +344,11 @@ export class MultipartReader {
maxMemory -= n;
maxValueBytes -= n;
}
- result[p.formName] = formFile;
+ if (formFile) {
+ fileMap.set(p.formName, formFile);
+ }
}
- return result;
+ return multipatFormData(fileMap, valueMap);
}
private currentPart: PartReader | undefined;
@@ -399,6 +412,43 @@ export class MultipartReader {
}
}
+function multipatFormData(
+ fileMap: Map<string, FormFile>,
+ valueMap: Map<string, string>
+): MultipartFormData {
+ function file(key: string): FormFile | undefined {
+ return fileMap.get(key);
+ }
+ function value(key: string): string | undefined {
+ return valueMap.get(key);
+ }
+ function* entries(): IterableIterator<
+ [string, string | FormFile | undefined]
+ > {
+ yield* fileMap;
+ yield* valueMap;
+ }
+ async function removeAll(): Promise<void> {
+ const promises: Array<Promise<void>> = [];
+ for (const val of fileMap.values()) {
+ if (!val.tempfile) continue;
+ promises.push(Deno.remove(val.tempfile));
+ }
+ await Promise.all(promises);
+ }
+ return {
+ file,
+ value,
+ entries,
+ removeAll,
+ [Symbol.iterator](): IterableIterator<
+ [string, string | FormFile | undefined]
+ > {
+ return entries();
+ },
+ };
+}
+
class PartWriter implements Writer {
closed = false;
private readonly partHeader: string;