import {randomBytes} from 'crypto'; import {isBlob} from './is.js'; const carriage = '\r\n'; const dashes = '-'.repeat(2); const carriageLength = Buffer.byteLength(carriage); /** * @param {string} boundary */ const getFooter = boundary => `${dashes}${boundary}${dashes}${carriage.repeat(2)}`; /** * @param {string} boundary * @param {string} name * @param {*} field * * @return {string} */ function getHeader(boundary, name, field) { let header = ''; header += `${dashes}${boundary}${carriage}`; header += `Content-Disposition: form-data; name="${name}"`; if (isBlob(field)) { header += `; filename="${field.name}"${carriage}`; header += `Content-Type: ${field.type || 'application/octet-stream'}`; } return `${header}${carriage.repeat(2)}`; } /** * @return {string} */ export const getBoundary = () => randomBytes(8).toString('hex'); /** * @param {FormData} form * @param {string} boundary */ export async function * formDataIterator(form, boundary) { for (const [name, value] of form) { yield getHeader(boundary, name, value); if (isBlob(value)) { yield * value.stream(); } else { yield value; } yield carriage; } yield getFooter(boundary); } /** * @param {FormData} form * @param {string} boundary */ export function getFormDataLength(form, boundary) { let length = 0; for (const [name, value] of form) { length += Buffer.byteLength(getHeader(boundary, name, value)); length += isBlob(value) ? value.size : Buffer.byteLength(String(value)); length += carriageLength; } length += Buffer.byteLength(getFooter(boundary)); return length; }