parse-multipart-data-umd

A UMD build of parse-multipart-data

אין להתקין סקריפט זה ישירות. זוהי ספריה עבור סקריפטים אחרים // @require https://update.greatest.deepsurf.us/scripts/517244/1483543/parse-multipart-data-umd.js

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.parseMultipartData = {}));
  5. })(this, (function (exports) { 'use strict';
  6.  
  7. function process(part) {
  8. // will transform this object:
  9. // { header: 'Content-Disposition: form-data; name="uploads[]"; filename="A.txt"',
  10. // info: 'Content-Type: text/plain',
  11. // part: 'AAAABBBB' }
  12. // into this one:
  13. // { filename: 'A.txt', type: 'text/plain', data: <Buffer 41 41 41 41 42 42 42 42> }
  14. const obj = function (str) {
  15. const k = str.split('=');
  16. const a = k[0].trim();
  17. const b = JSON.parse(k[1].trim());
  18. const o = {};
  19. Object.defineProperty(o, a, {
  20. value: b,
  21. writable: true,
  22. enumerable: true,
  23. configurable: true
  24. });
  25. return o;
  26. };
  27. const header = part.contentDispositionHeader.split(';');
  28. const filenameData = header[2];
  29. let input = {};
  30. if (filenameData) {
  31. input = obj(filenameData);
  32. const contentType = part.contentTypeHeader.split(':')[1].trim();
  33. Object.defineProperty(input, 'type', {
  34. value: contentType,
  35. writable: true,
  36. enumerable: true,
  37. configurable: true
  38. });
  39. }
  40. // always process the name field
  41. Object.defineProperty(input, 'name', {
  42. value: header[1].split('=')[1].replace(/"/g, ''),
  43. writable: true,
  44. enumerable: true,
  45. configurable: true
  46. });
  47. Object.defineProperty(input, 'data', {
  48. value: new Uint8Array(part.part),
  49. writable: true,
  50. enumerable: true,
  51. configurable: true
  52. });
  53. return input;
  54. }
  55.  
  56. const ParsingState = (ParsingState => {
  57. ParsingState[(ParsingState.INIT = 0)] = 'INIT';
  58. ParsingState[(ParsingState.READING_HEADERS = 1)] = 'READING_HEADERS';
  59. ParsingState[(ParsingState.READING_DATA = 2)] = 'READING_DATA';
  60. ParsingState[(ParsingState.READING_PART_SEPARATOR = 3)] = 'READING_PART_SEPARATOR';
  61. return ParsingState;
  62. })({});
  63.  
  64. function parse(multipartBodyBuffer, boundary) {
  65. let lastline = '';
  66. let contentDispositionHeader = '';
  67. let contentTypeHeader = '';
  68. let state = ParsingState.INIT;
  69. let buffer = [];
  70. const allParts = [];
  71. let currentPartHeaders = [];
  72. for (let i = 0; i < multipartBodyBuffer.length; i++) {
  73. const oneByte = multipartBodyBuffer[i];
  74. const prevByte = i > 0 ? multipartBodyBuffer[i - 1] : null;
  75. // 0x0a => \n
  76. // 0x0d => \r
  77. const newLineDetected = oneByte === 0x0a && prevByte === 0x0d;
  78. const newLineChar = oneByte === 0x0a || oneByte === 0x0d;
  79. if (!newLineChar) lastline += String.fromCharCode(oneByte);
  80. if (ParsingState.INIT === state && newLineDetected) {
  81. // searching for boundary
  82. if ('--' + boundary === lastline) {
  83. state = ParsingState.READING_HEADERS; // found boundary. start reading headers
  84. }
  85. lastline = '';
  86. } else if (ParsingState.READING_HEADERS === state && newLineDetected) {
  87. // parsing headers. Headers are separated by an empty line from the content. Stop reading headers when the line is empty
  88. if (lastline.length) {
  89. currentPartHeaders.push(lastline);
  90. } else {
  91. // found empty line. search for the headers we want and set the values
  92. for (const h of currentPartHeaders) {
  93. if (h.toLowerCase().startsWith('content-disposition:')) {
  94. contentDispositionHeader = h;
  95. } else if (h.toLowerCase().startsWith('content-type:')) {
  96. contentTypeHeader = h;
  97. }
  98. }
  99. state = ParsingState.READING_DATA;
  100. buffer = [];
  101. }
  102. lastline = '';
  103. } else if (ParsingState.READING_DATA === state) {
  104. // parsing data
  105. if (lastline.length > boundary.length + 4) {
  106. lastline = ''; // mem save
  107. }
  108. if ('--' + boundary === lastline) {
  109. const j = buffer.length - lastline.length;
  110. const part = buffer.slice(0, j - 1);
  111. allParts.push(process({
  112. contentDispositionHeader,
  113. contentTypeHeader,
  114. part
  115. }));
  116. buffer = [];
  117. currentPartHeaders = [];
  118. lastline = '';
  119. state = ParsingState.READING_PART_SEPARATOR;
  120. contentDispositionHeader = '';
  121. contentTypeHeader = '';
  122. } else {
  123. buffer.push(oneByte);
  124. }
  125. if (newLineDetected) {
  126. lastline = '';
  127. }
  128. } else if (ParsingState.READING_PART_SEPARATOR === state) {
  129. if (newLineDetected) {
  130. state = ParsingState.READING_HEADERS;
  131. }
  132. }
  133. }
  134. return allParts;
  135. }
  136. // read the boundary from the content-type header sent by the http client
  137. // this value may be similar to:
  138. // 'multipart/form-data; boundary=----WebKitFormBoundaryvm5A9tzU1ONaGP5B',
  139. function getBoundary(header) {
  140. const items = header.split(';');
  141. if (items) {
  142. for (let i = 0; i < items.length; i++) {
  143. const item = new String(items[i]).trim();
  144. if (item.indexOf('boundary') >= 0) {
  145. const k = item.split('=');
  146. return new String(k[1]).trim().replace(/^["']|["']$/g, '');
  147. }
  148. }
  149. }
  150. return '';
  151. }
  152. exports.getBoundary = getBoundary;
  153. exports.parse = parse;
  154. }));