Browse Source

companion: add calculated content-length for multipart uploads (#2466)

* companion: add calculated content-length for formdata and body cases

* companion: append fieldname and emit error in form data

Co-authored-by: Ifedapo .A. Olarewaju <ifedapoolarewaju@gmail.com>

* companion: change _handleUploadMultipart to _onMultipartComplete

* companion: use async fs.stat

Co-authored-by: Enrique Jhonatan Mejia Venegas <enrique.mejia@LIM-WS1807.local>
Co-authored-by: Ifedapo .A. Olarewaju <ifedapoolarewaju@gmail.com>
Enrique Mejia 4 years ago
parent
commit
886f059694
1 changed files with 60 additions and 42 deletions
  1. 60 42
      packages/@uppy/companion/src/server/Uploader.js

+ 60 - 42
packages/@uppy/companion/src/server/Uploader.js

@@ -5,6 +5,7 @@ const uuid = require('uuid')
 const isObject = require('isobject')
 const validator = require('validator')
 const request = require('request')
+const FormData = require('form-data')
 const emitter = require('./emitter')
 const serializeError = require('serialize-error')
 const { jsonStringify, hasMatch } = require('./helpers/utils')
@@ -471,54 +472,71 @@ class Uploader {
     const headers = headerSanitize(this.options.headers)
     const reqOptions = { url: this.options.endpoint, headers, encoding: null }
     if (this.options.useFormData) {
-      reqOptions.formData = Object.assign(
-        {},
-        this.options.metadata,
-        {
-          [this.options.fieldname]: {
-            value: file,
-            options: {
-              filename: this.uploadFileName,
-              contentType: this.options.metadata.type
-            }
-          }
+      const form = new FormData()
+      for (const property in this.options.metadata) {
+        form.append(property, this.options.metadata[property])
+      }
+      form.append(this.options.fieldname, file, {
+        filename: this.uploadFileName,
+        contentType: this.options.metadata.type
+      })
+      form.getLength((error, length) => {
+        if (error) {
+          logger.error(error, 'upload.multipart.size.error')
+          this.emitError(error)
+          return
         }
-      )
+        reqOptions.headers['content-length'] = length
+        const req = request[httpMethod](reqOptions, (error, response, body) => this._onMultipartComplete(error, response, body, bytesUploaded))
+        // @ts-ignore
+        req._form = form
+      })
     } else {
-      reqOptions.body = file
+      fs.stat(this.path, (err, stats) => {
+        if (err) {
+          logger.error(err, 'upload.multipart.size.error')
+          this.emitError(err)
+          return
+        }
+
+        const fileSizeInBytes = stats.size
+        reqOptions.headers['content-length'] = fileSizeInBytes
+        reqOptions.body = file
+        request[httpMethod](reqOptions, (error, response, body) => this._onMultipartComplete(error, response, body, bytesUploaded))
+      })
     }
+  }
 
-    request[httpMethod](reqOptions, (error, response, body) => {
-      if (error) {
-        logger.error(error, 'upload.multipart.error')
-        this.emitError(error)
-        return
-      }
-      const headers = response.headers
-      // remove browser forbidden headers
-      delete headers['set-cookie']
-      delete headers['set-cookie2']
-
-      const respObj = {
-        responseText: body.toString(),
-        status: response.statusCode,
-        statusText: response.statusMessage,
-        headers
-      }
+  _onMultipartComplete (error, response, body, bytesUploaded) {
+    if (error) {
+      logger.error(error, 'upload.multipart.error')
+      this.emitError(error)
+      return
+    }
+    const headers = response.headers
+    // remove browser forbidden headers
+    delete headers['set-cookie']
+    delete headers['set-cookie2']
+
+    const respObj = {
+      responseText: body.toString(),
+      status: response.statusCode,
+      statusText: response.statusMessage,
+      headers
+    }
 
-      if (response.statusCode >= 400) {
-        logger.error(`upload failed with status: ${response.statusCode}`, 'upload.multipart.error')
-        this.emitError(new Error(response.statusMessage), respObj)
-      } else if (bytesUploaded !== this.bytesWritten && bytesUploaded !== this.options.size) {
-        const errMsg = `uploaded only ${bytesUploaded} of ${this.bytesWritten} with status: ${response.statusCode}`
-        logger.error(errMsg, 'upload.multipart.mismatch.error')
-        this.emitError(new Error(errMsg))
-      } else {
-        this.emitSuccess(null, { response: respObj })
-      }
+    if (response.statusCode >= 400) {
+      logger.error(`upload failed with status: ${response.statusCode}`, 'upload.multipart.error')
+      this.emitError(new Error(response.statusMessage), respObj)
+    } else if (bytesUploaded !== this.bytesWritten && bytesUploaded !== this.options.size) {
+      const errMsg = `uploaded only ${bytesUploaded} of ${this.bytesWritten} with status: ${response.statusCode}`
+      logger.error(errMsg, 'upload.multipart.mismatch.error')
+      this.emitError(new Error(errMsg))
+    } else {
+      this.emitSuccess(null, { response: respObj })
+    }
 
-      this.cleanUp()
-    })
+    this.cleanUp()
   }
 
   /**