firebase-storage-upload-example icon indicating copy to clipboard operation
firebase-storage-upload-example copied to clipboard

Uploading results as empty file without mime type without errors

Open evilandfox opened this issue 5 years ago • 34 comments

Have anyone same problem?

evilandfox avatar Nov 07 '18 22:11 evilandfox

Same problem here. Looking for a workaround, will update if I find something.

devjv avatar Nov 08 '18 12:11 devjv

Problem is going from fetch polyfill package and there are PR that not merged yet Thanks for that author, I made quick fix that resolves problem

;(function(self) {
    'use strict'

    function parseHeaders(rawHeaders) {
        var headers = new Headers()
        var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
        preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
            var parts = line.split(':')
            var key = parts.shift().trim()
            if (key) {
                var value = parts.join(':').trim()
                headers.append(key, value)
            }
        })
        return headers
    }

    var supportsBlob =
        'FileReader' in self &&
        'Blob' in self &&
        (function() {
            try {
                new Blob()
                return true
            } catch (e) {
                return false
            }
        })()

    self.fetch = function(input, init) {
        return new Promise(function(resolve, reject) {
            var request = new Request(input, init)
            var xhr = new XMLHttpRequest()

            xhr.onload = function() {
                var options = {
                    status: xhr.status,
                    statusText: xhr.statusText,
                    headers: parseHeaders(xhr.getAllResponseHeaders() || '')
                }
                options.url =
                    'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
                var body = 'response' in xhr ? xhr.response : xhr.responseText
                resolve(new Response(body, options))
            }

            xhr.onerror = function() {
                reject(new TypeError('Network request failed'))
            }

            xhr.ontimeout = function() {
                reject(new TypeError('Network request failed'))
            }

            xhr.open(request.method, request.url, true)

            if (request.credentials === 'include') {
                xhr.withCredentials = true
            } else if (request.credentials === 'omit') {
                xhr.withCredentials = false
            }
            if ('responseType' in xhr && supportsBlob) {
                xhr.responseType = 'blob'
            }
            request.headers.forEach(function(value, name) {
                xhr.setRequestHeader(name, value)
            })

            xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
        })
    }
    self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this)

evilandfox avatar Nov 08 '18 13:11 evilandfox

facebook/react-native#22063

evilandfox avatar Nov 08 '18 13:11 evilandfox

Thank you so much for that!

devjv avatar Nov 08 '18 13:11 devjv

I'm having this issue as well when using the ios simulator. I checked in node_modules/whatwg-fetch and it has that change that the PR added. Help!

yonahforst avatar Nov 10 '18 13:11 yonahforst

I think the problem is that whatwg-fetch is not setting the type when building the blob.

this worked for me.

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();
  })
}


yonahforst avatar Nov 10 '18 16:11 yonahforst

@yonahforst thanks!!

farzd avatar Nov 12 '18 20:11 farzd

You the man @yonahforst

barrylachapelle avatar Nov 12 '18 21:11 barrylachapelle

AWESOME @yonahforst !!!!!! Just completely saved me, thank you! 🎖

bonham000 avatar Nov 16 '18 11:11 bonham000

Hey guys ... this is a huge issue for me. I am now having an issue with @yonahforst 's function... for some reason the func switching views in my app after PUT. When I use the old fetch the problem doesn't happen (alas no blob either)... is anyone else getting this with the function above?

Would rolling back SDKs fix this entire issue??

barrylachapelle avatar Nov 16 '18 21:11 barrylachapelle

Problem is going from fetch polyfill package and there are PR that not merged yet Thanks for that author, I made quick fix that resolves problem

;(function(self) {
    'use strict'

    function parseHeaders(rawHeaders) {
        var headers = new Headers()
        var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
        preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
            var parts = line.split(':')
            var key = parts.shift().trim()
            if (key) {
                var value = parts.join(':').trim()
                headers.append(key, value)
            }
        })
        return headers
    }

    var supportsBlob =
        'FileReader' in self &&
        'Blob' in self &&
        (function() {
            try {
                new Blob()
                return true
            } catch (e) {
                return false
            }
        })()

    self.fetch = function(input, init) {
        return new Promise(function(resolve, reject) {
            var request = new Request(input, init)
            var xhr = new XMLHttpRequest()

            xhr.onload = function() {
                var options = {
                    status: xhr.status,
                    statusText: xhr.statusText,
                    headers: parseHeaders(xhr.getAllResponseHeaders() || '')
                }
                options.url =
                    'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
                var body = 'response' in xhr ? xhr.response : xhr.responseText
                resolve(new Response(body, options))
            }

            xhr.onerror = function() {
                reject(new TypeError('Network request failed'))
            }

            xhr.ontimeout = function() {
                reject(new TypeError('Network request failed'))
            }

            xhr.open(request.method, request.url, true)

            if (request.credentials === 'include') {
                xhr.withCredentials = true
            } else if (request.credentials === 'omit') {
                xhr.withCredentials = false
            }
            if ('responseType' in xhr && supportsBlob) {
                xhr.responseType = 'blob'
            }
            request.headers.forEach(function(value, name) {
                xhr.setRequestHeader(name, value)
            })

            xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
        })
    }
    self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this)

@evilandfox can you explain a bit more how this fix works?

barrylachapelle avatar Nov 16 '18 21:11 barrylachapelle

@barrylachapelle That's very simple. It is just the original code for overwriting global fetch function with additional

if ('responseType' in xhr && supportsBlob) {
                xhr.responseType = 'blob'
            }

line of fix. And fetch function uses parseHeaders and supportsBlob out of its body, so we also have to add them.

evilandfox avatar Nov 17 '18 06:11 evilandfox

@jlsilva10 I used the solution above directly in my code. Use urlToBlob, pass in the uri for your file/document/image, and that will return you a promise which returns a blob for your file/document/image. Then, you can use that blob directly in a file upload using fetch, example:

/**
 * Get file blob using local file URI
 */
const blob = await uriToBlob(uri);

const options = {
  method: "PUT",
  body: blob,
  headers: {
    "Content-Type": "image/jpeg",
  },
};

/**
 * Upload blob to your API endpoint
 */
return fetch("your-api/some-endpoint", options);

bonham000 avatar Nov 27 '18 09:11 bonham000

Thanks! Worked like a charm ;)

jlsilva10 avatar Nov 27 '18 10:11 jlsilva10

Use that urlToBlob, and return this:

Promise { "_40": 0, "_55": null, "_65": 0, "_72": null, }

pierinho287 avatar Nov 27 '18 20:11 pierinho287

That returns a promise, you need to await for it.

dani-z avatar Nov 27 '18 23:11 dani-z

I think the problem is that whatwg-fetch is not setting the type when building the blob.

this worked for me.

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();
  })
}

This has to be in the sample code. It took 2 days to figure out the issue.

adamhongmy avatar Nov 28 '18 08:11 adamhongmy

@barrylachapelle That's very simple. It is just the original code for overwriting global fetch function with additional

if ('responseType' in xhr && supportsBlob) {
                xhr.responseType = 'blob'
            }

line of fix. And fetch function uses parseHeaders and supportsBlob out of its body, so we also have to add them.

Can you tell what file should we edit, please?

tiagodprovenzano avatar Jan 16 '19 18:01 tiagodprovenzano

@barrylachapelle That's very simple. It is just the original code for overwriting global fetch function with additional

if ('responseType' in xhr && supportsBlob) {
                xhr.responseType = 'blob'
            }

line of fix. And fetch function uses parseHeaders and supportsBlob out of its body, so we also have to add them.

Can you tell what file should we edit, please?

I guess this one /home/you/yourprojectfolder/node_modules/react-native/Libraries/vendor/core/whatwg-fetch.js

EnginYilmaz avatar Jan 28 '19 19:01 EnginYilmaz

I cannot patch the whatwg-fetch.js correctly I am getting this.initBody is not a function error please someone share the full code.

EnginYilmaz avatar Jan 28 '19 19:01 EnginYilmaz

@EnginYilmaz just use yonahforst code above

farzd avatar Jan 29 '19 13:01 farzd

@EnginYilmaz just use yonahforst code above

In my situation I am not using GET or PUT just URL for the fetch function for example

  onMessagePress() {
...
    this.setState({ error: '', loading: true, loadingSendingMessage: true });
    myURL = 'https://www.somedomain.com:443/send_message.php' + '?message=' + this.state.bodymessage + '&senderid=' + this.state.email + '&receipentid=' + this.props.navigation.getParam('email');
    return fetch(myURL)
      .then((response) => response.json())
      .then((responseJson) => {
        if (this._mounted) {
          this.setState({ error: responseJson.success });
        }
        if (responseJson.success == true) {
          if (this._mounted) {
            this.setState({ error: "Successfully sent message", loadingSendingMessage: false })
          }
        } else {
          if (this._mounted) {
            this.setState({ error: responseJson.success })
          }
        }
      })
  }

EnginYilmaz avatar Jan 29 '19 16:01 EnginYilmaz

@EnginYilmaz same thing, just use the code from yonahforst and do urlToBlob(myURL) instead of fetch(myURL)

jrnk avatar Jan 29 '19 16:01 jrnk

@EnginYilmaz same thing, just use the code from yonahforst and do urlToBlob(myURL) instead of fetch(myURL)

@jrnk I tried but function never finishes loading and getting warning message

Possible Unhandled Promise Rejection (id: 0): TypeError:response.json is not a function(In 'response.json()', response.json is undefined

EnginYilmaz avatar Jan 29 '19 17:01 EnginYilmaz

@EnginYilmaz how are you using it ? please post your code

this should work =


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();
  })
}

//inside async function
try {
    const response = await urlToBlob(imageUrl)
    await fetch(url, { method: 'PUT', body: response })
} catch (e) {
 console.log('error is:', e);
}

farzd avatar Jan 30 '19 15:01 farzd

I am getting Network request failed 504:29 error message but not using local or remote image so no image URL. I am using it as https://github.com/expo/firebase-storage-upload-example/issues/13#issuecomment-458614689

EnginYilmaz avatar Jan 30 '19 16:01 EnginYilmaz

I think it is better not to use fetch anywhere I will have to change all the code to XMLHttpRequest on each scene. I have fetch function nearly on 20 pages in total! I hope this will help anyone consider to use fetch in their project.

EnginYilmaz avatar Feb 02 '19 13:02 EnginYilmaz

@EnginYilmaz surely you can just write a fetch function and call it in your 20 pages, it will be a simple find and search ?

farzd avatar Feb 02 '19 13:02 farzd

@farzd I have lots of codes like

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(JSON.stringify(myJson));
  });

What will happen to after .then I need to write them from scratch.

EnginYilmaz avatar Feb 02 '19 13:02 EnginYilmaz

Besides, I afroid getting same error message after getting away from fetch

EnginYilmaz avatar Feb 02 '19 13:02 EnginYilmaz