TypeError: Cannot convert undefined or null to obj

2019-05-30 09:32发布

I am trying to upload Image/video files into S3 bucket by using S3 multipart upload method. After that I found ReactS3Uploader npm package. After import this package with my React component I keep getting TypeError: Cannot convert undefined or null to object error message while picking a files from browser.

Error message:

        Pre-process: 2.png
        Upload progress: 0% Waiting

        Uncaught TypeError: Cannot convert undefined or null to object
        at Function.assign (<anonymous>)
        at S3Upload.push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.uploadToS3 (s3upload.js:64)
        at S3Upload.push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.uploadFile (s3upload.js:95)
        at S3Upload.<anonymous> (s3upload.js:57)
        at S3Upload.preprocess (ReactS3Uploader.js:35)
        at S3Upload.push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.handleFileSelect (s3upload.js:55)
        at new S3Upload (s3upload.js:47)
        at Object.uploadFile (ReactS3Uploader.js:56)
        at HTMLUnknownElement.callCallback (react-dom.development.js:147)
        at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
        at invokeGuardedCallback (react-dom.development.js:250)
        at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:265)
        at executeDispatch (react-dom.development.js:571)
        at executeDispatchesInOrder (react-dom.development.js:596)
        at executeDispatchesAndRelease (react-dom.development.js:695)
        at executeDispatchesAndReleaseTopLevel (react-dom.development.js:704)
        at forEachAccumulated (react-dom.development.js:676)
        at runEventsInBatch (react-dom.development.js:844)
        at runExtractedEventsInBatch (react-dom.development.js:852)
        at handleTopLevel (react-dom.development.js:5025)
        at batchedUpdates$1 (react-dom.development.js:19904)
        at batchedUpdates (react-dom.development.js:2246)
        at dispatchEvent (react-dom.development.js:5105)
    push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.uploadToS3 @ s3upload.js:64
    push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.uploadFile @ s3upload.js:95
    (anonymous) @ s3upload.js:57
    preprocess @ ReactS3Uploader.js:35
    push../node_modules/react-s3-uploader-multipart/s3upload.js.S3Upload.handleFileSelect @ s3upload.js:55
    S3Upload @ s3upload.js:47
    uploadFile @ ReactS3Uploader.js:56
    callCallback @ react-dom.development.js:147
    invokeGuardedCallbackDev @ react-dom.development.js:196
    invokeGuardedCallback @ react-dom.development.js:250
    invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:265
    executeDispatch @ react-dom.development.js:571
    executeDispatchesInOrder @ react-dom.development.js:596
    executeDispatchesAndRelease @ react-dom.development.js:695
    executeDispatchesAndReleaseTopLevel @ react-dom.development.js:704
    forEachAccumulated @ react-dom.development.js:676
    runEventsInBatch @ react-dom.development.js:844
    runExtractedEventsInBatch @ react-dom.development.js:852
    handleTopLevel @ react-dom.development.js:5025
    batchedUpdates$1 @ react-dom.development.js:19904
    batchedUpdates @ react-dom.development.js:2246
    dispatchEvent @ react-dom.development.js:5105

Here is my code sample,

import React, { Component } from 'react';
import './App.css';
var ReactS3Uploader = require('react-s3-uploader-multipart');
class App extends Component {
   render() {
    return (
      <div className="App">
        <ReactS3Uploader
          signingUrl="/s3/sign"
          signingUrlMethod="GET"
          accept="image/*"
          s3path="/uploads/"
          preprocess={this.onUploadStart}
          onProgress={this.onUploadProgress}
          onError={this.onUploadError}
          onFinish={this.onUploadFinish}
          signingUrlWithCredentials={true}
          uploadRequestHeaders={{ 'x-amz-acl': 'public-read' }}  
          contentDisposition="auto"
          scrubFilename={(filename) => 
             filename.replace(/[^\w\d_\-.]+/ig, '')}
          server="http://cross-origin-server.com"
          inputRef={cmp => this.uploadInput = cmp}
          autoUpload={true}
       />
    </div>
  );
}
}

export default App;

Package.json :

   {
    "name": "s3",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
      "react": "^16.6.1",
      "react-dom": "^16.6.1",
      "react-s3-uploader-multipart": "^4.8.0",
      "react-scripts": "2.1.1"
    },
    "scripts": {
      "start": "react-scripts start",
      "build": "react-scripts build",
      "test": "react-scripts test",
      "eject": "react-scripts eject"
    },
    "eslintConfig": {
      "extends": "react-app"
    },
    "browserslist": [
      ">0.2%",
      "not dead",
      "not ie <= 11",
      "not op_mini all"
    ]
  }

Thanks in advance.

1条回答
孤傲高冷的网名
2楼-- · 2019-05-30 10:19

react-s3-uploader-multipart module can't be used without some modification to its source code.

You see, the error logged to the console happens as a result of the object assignment on Line 63 of s3Upload.js

var evaporateOptions = Object.assign(this.evaporateOptions, {
    signerUrl: this.signingUrl
});

This reason that this occurs is that when S3Upload is called in line 65 of ReactS3Upload.js, it doesn't forward evaporateOptions property which must be set when creating the ReactS3Uploader React Element.

    this.myUploader = new S3Upload({
        fileElement: ReactDOM.findDOMNode(this),
        signingUrl: this.props.signingUrl,
        getSignedUrl: this.props.getSignedUrl,
        preprocess: this.props.preprocess,
        onProgress: this.props.onProgress,
        onFinishS3Put: this.props.onFinish,
        onError: this.props.onError,
        signingUrlMethod: this.props.signingUrlMethod,
        signingUrlHeaders: this.props.signingUrlHeaders,
        signingUrlQueryParams: this.props.signingUrlQueryParams,
        signingUrlWithCredentials: this.props.signingUrlWithCredentials,
        uploadRequestHeaders: this.props.uploadRequestHeaders,
        contentDisposition: this.props.contentDisposition,
        server: this.props.server,
        scrubFilename: this.props.scrubFilename,
        s3path: this.props.s3path,
        evaporateOptions: this.props.evaporateOptions // this is missing
    });

Also in Line 68 and Line 103 of s3upload.js, this.s3Path is undefined, rather the property access in those lines should be this.s3path

This leaves you with the option of making a fork of the repository, doing this change and making a pull request for it to be merged to the upstream and deployed to npm registry or looking in the npm package registry for another one.

If you make this change then evaporateOptions must be passed as props. e.g.

import crypto from 'crypto';

const config = {
  signerUrl: 'auth_upload',
  aws_key: 'AKALN0L7ASDFLKJH',
  bucket: 'my-big-bucket',
  computeContentMd5: true,
  cryptoHexEncodedHash256: data => crypto.createHash('sha256').update(data).digest('hex'),
  cryptoMd5Method: data => crypto.createHash('md5').update(data).digest('base64')
};

<ReactS3Uploader
  accept="image/*"
  s3path="uploads/"
  signingUrlWithCredentials={true}
  uploadRequestHeaders={{ 'x-amz-acl': 'public-read' }}  
  contentDisposition="auto"
  server="<backend-svc>"
  inputRef={ref => (this.uploadInput = ref)}
  evaporateOptions={config}
/>

Also, an endpoint in a backing service needs to be implemented for signing the S3 upload URL. See this /sign express route to get an example of how that is implemented.

查看更多
登录 后发表回答