tong233 / react-native-resumable

Make resumable.js support React-Native. A JavaScript library for providing multiple simultaneous, stable, fault-tolerant and resumable/restartable uploads.
MIT License
1 stars 0 forks source link

Do we have working example for this in react native #1

Open bsjaffer opened 4 years ago

bsjaffer commented 4 years ago

I am trying to use resumable js to upload fine , but i am getting files added event but i am not getting any error or upload progress , am i missing something here?


  onPress = async () => {
    const options = {
      title: 'Select Avatar',
      customButtons: [{ name: 'fb', title: 'Choose Photo from Facebook' }],
      storageOptions: {
        skipBackup: true,
        path: 'images',
      },
    };
    ImagePicker.showImagePicker(options, (response) => {
      //console.log('Response = ', response);

      var r = new Resumable({
        target: 'SERVERDOMAIN/api/fileUpload/Upload',
        query: { upload_token: 'my_token' }
      });

      r.on('fileAdded', function (file, event) {

        console.log('file added');
        //   ...
      });
      r.on('fileSuccess', function (file, message) {
        console.log('file fileSuccess', file, message);
        //  ...
      });
      r.on('fileError', function (file, message) {
        console.log('file fileError', file, message);
        //  ...
      });

      r.on('error', function (message, file) {
        console.log(message, 'error')
      });

      console.log('fileName', response.fileName);
      response.name = "test.jpg";
      // get Blob for example via react-native-syan-image-picker react-native-document-picker
      r.addFile(response)

      setTimeout(() => {
        try {
          r.upload();
          console.log(r.isUploading())
        }
        catch (e) {
          console.log(e);
        }
      }, 2000)

    });

  }
  render() {
    return (

      <SafeAreaView>
        <View>
          <Text>Upload Test</Text>

          <TouchableOpacity onPress={this.onPress}>
            <Text>Click here to upload</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    )
  }
noobmaster6981 commented 4 years ago

@tong233 A sample code as to how to use your library would be extremely helpful

noobmaster6981 commented 4 years ago

@bsjaffer Were you able to make this work?

tong233 commented 3 years ago

@bsjaffer Sorry for the late reply, before addFile() you need convert native File to Blob:

const blob = await fetch(file.uri).then(res => res.blob());
r.addFile(blob);
r.upload();
tong233 commented 3 years ago

@bsjaffer sample example

componentDidMount() {
  this.inintResumable()
}

inintResumable = () => {
  const option = {
    withCredentials: true,
    chunkNumberParameterName: 'chunk',
    totalSizeParameterName: 'size',
    typeParameterName: 'type',
    fileNameParameterName: 'fileName',
    relativePathParameterName: 'md5', // relativePath as md5 for server
    totalChunksParameterName: 'chunks',
    target: uploadPath,
    testChunks: false,
    chunkSize: 1024 * 1024,
    forceChunkSize: true,
    simultaneousUploads: 5,
    maxChunkRetries: 3,
    chunkRetryInterval: 500,
    allowDuplicateUploads: true,
    method: 'octet',
    xhrTimeout: 30 * 1000,
  }
  this.resumable = new Resumable(option)
  this.resumable.on('fileAdded', (file, event) => {
    this.getMaxChunkNumber(file)
  })
  this.resumable.on('fileSuccess', (file, message) => {
    this.closeFileBlob()
  })
  this.resumable.on('error', (message, file) => {
    this.closeFileBlob()
  })
  this.resumable.on('timeout', (message, file) => {
    this.closeFileBlob()
  })
  this.resumable.on('fileProgress', (file, message) => {
    const progress = file.progress()
    console.log(progress)
  })
}

// Get the maximum number of chunks of the file exists on the server
getMaxChunkNumber = async file => {
  try {
    const data = {
      fileName: file.fileName,
      md5: file.relativePath,
      ext: file.type
    }
    const res = await requestMaxChunkNumber(data)
    if (res.number) {
      file.markChunksCompleted(res.number || 0) // mark the resume transfer from the nth block
    }

    this.resumable.upload()
  } catch (error) {
  }
}

// get file for example via react-native-syan-image-picker react-native-document-picker
upload = async (file) => {
  this.blob = await fetch(file.uri).then(res => res.blob()) // convert native file to blob

  this.blob.name = this.blob._data.name
  // import RNFS from 'react-native-fs'
  const md5 = await RNFS.hash(file.uri, 'md5')
  this.blob.relativePath = md5 // relativePath as md5

  this.resumable.addFile(this.blob)
}

closeFileBlob = () => {
  if (this.blob instanceof Blob) {
    try {
      this.blob.close() // close blob destroy memory
    } catch (error) {
      console.warn('close blob error', error)
    }
  }
}
nogenem commented 3 years ago

@tong233 Man... Did you try to use the default 'multipart' method too? My server is already setted up to accept that, so i didn't want to change it now, since i have a JS client too using multipart...

When i tried, first i got the following error:

[TypeError: data.close is not a function. (In 'data.close()', 'data.close' is undefined)]

Which is caused by line 919 of the lib's code, since he/she tries to close the FormData instead of the Blob file... To fix this, since i didnt want to fork the code, i added the following code to my main index.js

window.FormData.prototype.close = () => {};

After doing this, i noticed that the Blob generated by the fetch doesn't get the right mimeType setted... So to fix this, i changed the fetch call to use this function that i found on google:

function urlToBlob(url) {
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest();
    xhr.onerror = reject;
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        resolve(xhr.response);
      }
    };
    xhr.open('GET', url);
    xhr.responseType = 'blob'; // convert type
    xhr.send();
  });
}

And then, finally, i noticed that the xhr isn't adding the right Content-Type, with the boundary, to the request made by this lib ;___; I tried A LOT of things today and couldn't find any way to fix this... I keep getting a $.xhr.responseText of:

type == null

I think that the problem should be in the native code of react native, maybe? Cause the Blob part generated by FormData seems to be right:

{
   "part":{
      "_data":{
         "__collector":[
            "Object"
         ],
         "blobId":"17db3c43-4ebe-4fde-afb6-d8499cc2cf5b",
         "lastModified":"undefined",
         "offset":0,
         "size":579340,
         "type":"image/jpeg"
      },
      "fieldName":"file",
      "headers":{
         "content-disposition":"form-data; name=\"file\"",
         "content-type":"image/jpeg"
      }
   }
}

Maybe anyone has any ideas? ;/