(form, headersHandler, options)
| 66 | } |
| 67 | |
| 68 | const formDataToStream = (form, headersHandler, options) => { |
| 69 | const { |
| 70 | tag = 'form-data-boundary', |
| 71 | size = 25, |
| 72 | boundary = tag + '-' + platform.generateString(size, BOUNDARY_ALPHABET), |
| 73 | } = options || {}; |
| 74 | |
| 75 | if (!utils.isFormData(form)) { |
| 76 | throw new TypeError('FormData instance required'); |
| 77 | } |
| 78 | |
| 79 | if (boundary.length < 1 || boundary.length > 70) { |
| 80 | throw new Error('boundary must be 1-70 characters long'); |
| 81 | } |
| 82 | |
| 83 | const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); |
| 84 | const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF); |
| 85 | let contentLength = footerBytes.byteLength; |
| 86 | |
| 87 | const parts = Array.from(form.entries()).map(([name, value]) => { |
| 88 | const part = new FormDataPart(name, value); |
| 89 | contentLength += part.size; |
| 90 | return part; |
| 91 | }); |
| 92 | |
| 93 | contentLength += boundaryBytes.byteLength * parts.length; |
| 94 | |
| 95 | contentLength = utils.toFiniteNumber(contentLength); |
| 96 | |
| 97 | const computedHeaders = { |
| 98 | 'Content-Type': `multipart/form-data; boundary=${boundary}`, |
| 99 | }; |
| 100 | |
| 101 | if (Number.isFinite(contentLength)) { |
| 102 | computedHeaders['Content-Length'] = contentLength; |
| 103 | } |
| 104 | |
| 105 | headersHandler && headersHandler(computedHeaders); |
| 106 | |
| 107 | return Readable.from( |
| 108 | (async function* () { |
| 109 | for (const part of parts) { |
| 110 | yield boundaryBytes; |
| 111 | yield* part.encode(); |
| 112 | } |
| 113 | |
| 114 | yield footerBytes; |
| 115 | })() |
| 116 | ); |
| 117 | }; |
| 118 | |
| 119 | export default formDataToStream; |
no test coverage detected