fix(cli/web/fetch): multipart/form-data request body support for binary files (#5886)

This commit is contained in:
Marcos Casagrande 2020-06-08 18:08:26 +02:00 committed by GitHub
parent 4feccdd3b7
commit d907133944
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 172 additions and 38 deletions

View file

@ -1,5 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { Buffer } from "../../buffer.ts";
import { bytesSymbol } from "../blob.ts";
import { DomFileImpl } from "../dom_file.ts";
import { DenoBlob } from "../blob.ts";
import { TextEncoder, TextDecoder } from "../text_encoding.ts";
import { getHeaderValueParams } from "../util.ts";
@ -14,6 +17,84 @@ interface MultipartHeaders {
disposition: Map<string, string>;
}
export class MultipartBuilder {
readonly boundary: string;
readonly formData: FormData;
readonly writer: Buffer;
constructor(formData: FormData, boundary?: string) {
this.boundary = boundary ?? this.#createBoundary();
this.formData = formData;
this.writer = new Buffer();
}
getContentType(): string {
return `multipart/form-data; boundary=${this.boundary}`;
}
getBody(): Uint8Array {
for (const [fieldName, fieldValue] of this.formData.entries()) {
if (fieldValue instanceof DomFileImpl) {
this.#writeFile(fieldName, fieldValue);
} else this.#writeField(fieldName, fieldValue as string);
}
this.writer.writeSync(encoder.encode(`\r\n--${this.boundary}--`));
return this.writer.bytes();
}
#createBoundary = (): string => {
return (
"----------" +
Array.from(Array(32))
.map(() => Math.random().toString(36)[2] || 0)
.join("")
);
};
#writeHeaders = (headers: string[][]): void => {
let buf = this.writer.empty() ? "" : "\r\n";
buf += `--${this.boundary}\r\n`;
for (const [key, value] of headers) {
buf += `${key}: ${value}\r\n`;
}
buf += `\r\n`;
this.writer.write(encoder.encode(buf));
};
#writeFileHeaders = (
field: string,
filename: string,
type?: string
): void => {
const headers = [
[
"Content-Disposition",
`form-data; name="${field}"; filename="${filename}"`,
],
["Content-Type", type || "application/octet-stream"],
];
return this.#writeHeaders(headers);
};
#writeFieldHeaders = (field: string): void => {
const headers = [["Content-Disposition", `form-data; name="${field}"`]];
return this.#writeHeaders(headers);
};
#writeField = (field: string, value: string): void => {
this.#writeFieldHeaders(field);
this.writer.writeSync(encoder.encode(value));
};
#writeFile = (field: string, value: DomFileImpl): void => {
this.#writeFileHeaders(field, value.name, value.type);
this.writer.writeSync(value[bytesSymbol]);
};
}
export class MultipartParser {
readonly boundary: string;
readonly boundaryChars: Uint8Array;