Firebase Storage v3 returning “multipart body does

2019-03-31 05:27发布

问题:

I am building an Ionic app to deploy on Android 5 and 4. When I try to use a Firebase v3 storage ref (using firebase.js API) to save a Blob on Android 4.2.2 and 4.3 it fails with:

code: "storage/unknown" 
message: "Firebase Storage: An unknown error occurred, please check the error payload for server response." 
serverResponse: "multipart body does not contain 2 or 3 parts." 
name: "FirebaseError"

This works fine on an Android 5.0.1 and 4.4.2 device though.

Question: Is this a hard limitation of the Android (or included webkit) versions? Or some incompatibility in the firebase.js API I'm using? I'm trying to understand if the problem can be fixed or if nothing can be done.

The only other reference to that error message I could find was here: https://github.com/davideast/firebase-react-native-sample/issues/5

But seeing as this is an ionic wrapper app, I do have access to the Blob type.

The file is created by taking a picture using the cordova-plugin-camera extension, saving it to the phones local storage with Camera.DestinationType.FILE_URI

It is then read back into a Blob using the cordova-plugin-file extension before being saved to Firebase Storage (specifically the Google Cloud Store backed storage because this is using firebase v3).

The Blob seems to be created successfully (although it may fall back to using the BlobBuilder if the new Blob constructor is not available - see code snippet)

The relevant section of code (from within an AngularJS controller) looks like:

var makeBlob = function(data, mimeString) {
  try {
    return new Blob([data], {
      type: mimeString
    });
  } catch (err) {
    var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
    var bb = new BlobBuilder();
    bb.append(data);
    return bb.getBlob(mimeString);
  }
};

$scope.uploadLocalImageAsync = function(localImageFileUrl, responseKey, description) {
  var deferred = $q.defer();

  $window.resolveLocalFileSystemURL(localImageFileUrl, function fileEntrySuccess(fileEntry) {
      fileEntry.file(function fileSuccess(file) {
        var reader = new FileReader();
        reader.onloadend = function(evt) {
          try {
            var imageBlob = makeBlob(evt.target.result, 'image/jpeg');
            alert("imageBlob: " + JSON.stringify(imageBlob));

            var metadata = {
              contentType: 'image/jpeg',
              customMetadata: {
                'response': responseKey,
                'description': description
              }
            };

            // Create a root reference
            var storageRef = firebase.storage().ref();
            var fileRef = storageRef.child('images/' + responseKey + '/' + file.name);

            var uploadTask = fileRef.put(imageBlob, metadata);
            uploadTask.on('state_changed', function(snapshot) {
              // Observe state change events such as progress, pause, and resume
              // See below for more detail
              alert(JSON.stringify(snapshot));

            }, function(error) {
              // Handle unsuccessful uploads
              alert(JSON.stringify(error));
              deferred.reject("Upload failed: " + error.code + ": " + error.message)
            }, function() {
              // Handle successful uploads on complete
              // For instance, get the download URL: https://firebasestorage.googleapis.com/...
              var downloadURL = uploadTask.snapshot.downloadURL;
              deferred.resolve({
                gsPath: fileRef.toString(),
                downloadUrl: downloadURL
              });
            });
          } catch (err) {
            deferred.reject("onloadend error: " + err);
          }
        };

        try {
          reader.readAsArrayBuffer(file);
        } catch (err) {
          deferred.reject("readAsArrayBuffer error: " + err);
        }

      }, function fileFailure() {
        deferred.reject("Couldn't make File object");
      });
    },
    function fileEntryFailure() {
      deferred.reject("Couldn't find fileEntry for image");
    });

  return deferred.promise;
};

localImageFileUrl is of the form "file:///storage/sdcard0/Android/data/com.ionicframework.myapp/cache/1471426280702.jpg"

The function actually returns a promise which is resolved later in another part of the AngularJS controller.

The failure occurs after starting the uploadTask.

Again, this works on Android 5.0.1, 4.4.2 but fails on 4.3, 4.2.2

I'm hoping there is some extra setting in Ionic or something that I'm missing. But I really just don't know what is causing the difference in behavior or if there will be anything I can do about it.


Things I've tried:

I've tried downgrading the cordova android platform to 3.5 to target android-17 (4.2.2) specifically (using something similar to here https://stackoverflow.com/a/32672214/6729807 ) but it produces the same error.

(I also downgraded the different plugins to appropriate levels too)


Some information on versions of tools being used:

ionic info:

Your system information:

Cordova CLI: 6.3.1
Ionic Framework Version: 1.3.1
Ionic CLI Version: 2.0.0
Ionic App Lib Version: 2.0.0-beta.20
OS:
Node Version: v4.4.7

ionic platform version android:

Installed platforms:
  android 5.2.1

/platforms/android/AndroidManifest.xml

<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />

/platforms/android/project.properties (and CordovaLib project.properties)

target=android-23

installed plugins:

cordova-plugin-camera
cordova-plugin-compat
cordova-plugin-file
cordova-plugin-geolocation
cordova-plugin-whitelist

using https://www.gstatic.com/firebasejs/3.3.0/firebase.js

and https://github.com/firebase/angularfire (release v2.0.1)

回答1:

The resolving key was using RNFetchBlob.polyfill.XMLHttpRequest;

import RNFetchBlob from 'react-native-fetch-blob';
const Blob = RNFetchBlob.polyfill.Blob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;

install package 
- https://github.com/wkh237/react-native-fetch-blob
and execute code
-  https://github.com/wkh237/rn-firebase-storage-upload-sample/blob/master/index.common.js

also read http://stackoverflow.com/questions/39160006/react-native-firebase-storage-upload-using-putstring-call