mirror of
https://github.com/denoland/deno.git
synced 2025-10-02 15:14:33 +00:00
fix(cli/web/fetch): multipart/form-data request body support for binary files (#5886)
This commit is contained in:
parent
4feccdd3b7
commit
d907133944
3 changed files with 172 additions and 38 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue