How to get a file or blob from an object URL?

256,694

Solution 1

Modern solution:

let blob = await fetch(url).then(r => r.blob());

The url can be an object url or a normal url.

Solution 2

As gengkev alludes to in his comment above, it looks like the best/only way to do this is with an async xhr2 call:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//your.blob.url.here', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  if (this.status == 200) {
    var myBlob = this.response;
    // myBlob is now the blob that the object URL pointed to.
  }
};
xhr.send();

Update (2018): For situations where ES5 can safely be used, Joe has a simpler ES5-based answer below.

Solution 3

Maybe someone finds this useful when working with React/Node/Axios. I used this for my Cloudinary image upload feature with react-dropzone on the UI.

    axios({
        method: 'get',
        url: file[0].preview, // blob url eg. blob:http://127.0.0.1:8000/e89c5d87-a634-4540-974c-30dc476825cc
        responseType: 'blob'
    }).then(function(response){
         var reader = new FileReader();
         reader.readAsDataURL(response.data); 
         reader.onloadend = function() {
             var base64data = reader.result;
             self.props.onMainImageDrop(base64data)
         }
    })

Solution 4

Using fetch for example like below:

 fetch(<"yoururl">, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + <your access token if need>
    },
       })
.then((response) => response.blob())
.then((blob) => {
// 2. Create blob link to download
 const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `sample.xlsx`);
 // 3. Append to html page
 document.body.appendChild(link);
 // 4. Force download
 link.click();
 // 5. Clean up and remove the link
 link.parentNode.removeChild(link);
})

You can paste in on Chrome console to test. the file with download with 'sample.xlsx' Hope it can help!

Solution 5

The problem with fetching the blob URL again is that this will create a full copy of the Blob's data, and so instead of having it only once in memory, you'll have it twice. With big Blobs this can blow your memory usage quite quickly.

It's rather unfortunate that the File API doesn't give us access to the currently linked Blobs, certainly they thought web-authors should store that Blob themselves at creation time anyway, which is true:

The best here is to store the object you used when creating the blob:// URL.

If you are afraid this would prevent the Blob from being Garbage Collected, you're right, but so does the blob:// URL in the first place, until you revoke it. So holding yourself a pointer to that Blob won't change a thing.

But for those who aren't responsible for the creation of the blob:// URI (e.g because a library made it), we can still fill that API hole ourselves by overriding the default URL.createObjectURL and URL.revokeObjectURL methods so that they do store references to the object passed.

Be sure to call this function before the code that does generate the blob:// URI is called.

// Adds an URL.getFromObjectURL( <blob:// URI> ) method
// returns the original object (<Blob> or <MediaSource>) the URI points to or null
(() => {
  // overrides URL methods to be able to retrieve the original blobs later on
  const old_create = URL.createObjectURL;
  const old_revoke = URL.revokeObjectURL;
  Object.defineProperty(URL, 'createObjectURL', {
    get: () => storeAndCreate
  });
  Object.defineProperty(URL, 'revokeObjectURL', {
    get: () => forgetAndRevoke
  });
  Object.defineProperty(URL, 'getFromObjectURL', {
    get: () => getBlob
  });
  const dict = {};
  function storeAndCreate(blob) {
    const url = old_create(blob); // let it throw if it has to
    dict[url] = blob;
    return url
  }
  function forgetAndRevoke(url) {
    old_revoke(url);
    try {
      if(new URL(url).protocol === 'blob:') {
        delete dict[url];
      }
    } catch(e){}
  }
  function getBlob(url) {
    return dict[url] || null;
  }
})();
//  Usage:
const blob = new Blob( ["foo"] );
const url = URL.createObjectURL( blob );
console.log( url );
const retrieved = URL.getFromObjectURL( url );
console.log( "retrieved Blob is Same Object?", retrieved === blob );
fetch( url ).then( (resp) => resp.blob() )
  .then( (fetched) => console.log( "fetched Blob is Same Object?", fetched === blob ) );

And an other advantage is that it can even retrieve MediaSource objects, while the fetching solutions would just err in that case.

Share:
256,694

Related videos on Youtube

BrianFreud
Author by

BrianFreud

Updated on October 07, 2021

Comments

  • BrianFreud
    BrianFreud over 1 year

    I am allowing the user to load images into a page via drag&drop and other methods. When an image is dropped, I'm using URL.createObjectURL to convert to an object URL to display the image. I am not revoking the url, as I do reuse it.

    So, when it comes time to create a FormData object so I can allow them to upload a form with one of those images in it, is there some way I can then reverse that Object URL back into a Blob or File so I can then append it to a FormData object?

    • gengkev
      gengkev almost 11 years
      nevermind about the previous two comments - all you need to do is send an XMLHttpRequest to the blob URL.
    • user764754
      user764754 over 6 years
      Why not simply store the original file objects somewhere, then use the object URL to display them and on form submit use the original files?
    • Malcolm Salvador
      Malcolm Salvador almost 6 years
      @user764754 because trying to get the URL of the file being uploaded by the user displays as "C:\fakepath"
    • Armen Michaeli
      Armen Michaeli about 2 years
      This is a XY problem. A script obtains references to one or multiple files as these are dropped onto a page. You only need to create URLs for these in order to create links for these for the user to use (look at what they dragged, f.e.), but for including the file(s) with submission of a form, you need to add them one way or another -- whether gotten back from URLs or the original objects. You can look at the DataTransfer class to reset your file input control, otherwise your form is almost useless anyway (you can forego it for script-assisted submission).
  • BrianFreud
    BrianFreud over 8 years
    When would you ever have a objectURL which is not local to the current domain and scope?
  • albert yu
    albert yu over 8 years
    i did it same as above, but got 404 not found with the xhr. what's going on?
  • mraxus about 7 years
    Please note that some browsers (read old IE...), if you need to handle those see post stackoverflow.com/questions/17657184/…
  • Rahul Bali
    Rahul Bali almost 7 years
    Can I save this file to the local file system?
  • BrianFreud
    BrianFreud almost 7 years
    Note that that's not actually giving you back the same original file; it's making a new image file on the fly. When I last tested that a couple years ago, I found that at least in Chrome, the new image file isn't compressed at all - I had 10k jpgs turning into 2.5 mb jpgs.
  • Wagner Bertolini Junior
    Wagner Bertolini Junior over 6 years
    @BrianFreud "When would you ever have a objectURL which is not local to the current domain and scope?" In my case I am trying to give a Amazon S3 blob a different filename, that is currently a "guid" on the S3 without extension. So, in my case I am using a cross-domain call.
  • BrianFreud
    BrianFreud over 6 years
    ObjectURLs are by definition local. Object URLs are URLs that point to files on disk. Given that there is no such thing as a cross-domain objectURL, you're combing two different concepts, thus your problem using the given solution.
  • BrianFreud
    BrianFreud over 6 years
    "Object URLs are URLs that point to files on disk." ObjectURLs by definition can only be local.
  • Wagner Bertolini Junior
    Wagner Bertolini Junior over 6 years
    @BrianFreud I got it that is not exactly the answer of this question. But I still think that its a good answer to leave since I got here looking for the answer of a little different question 'How to get a file or blob from an URL?'. If you check you own answer, there are 10 upvotes on 'It doesn't work in case of cross domain requests. '. So more than 10 persons got here looking for that. I decided then to leave it here.
  • BrianFreud
    BrianFreud over 6 years
    Problem is, your answer knowingly doesn't answer this question. A normal URL and an object URL are two entirely different things. Regarding the # of upvotes on "It doesn't work in case of cross domain requests.", wouldn't you think it better explain why that's literally impossible here, and to point to somewhere that does show the answer for normal URLs, rather than confusing things here by conflating normal URLs and object URLs?
  • Wagner Bertolini Junior
    Wagner Bertolini Junior over 6 years
    @BrianFreud feel free to suggest an edit on my answer. I will study about the subject, maybe I misunderstood what is an "objectURL" vs an "object URL"... English is not my native. I will make a research about the subject to give a better answer. But I think that are others that come here looking for something different. I did not search for "objectURL" to get here, that was my point before. But I got you too.
  • Arihant
    Arihant about 5 years
    Does this work for cross domain requests? Twitter videos have blob URL. I need to be able to gather the blob object that blob URL is pointing to. I'm using fetch api in the browser, which is giving me this error – Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302‌​fa' because it violates the following Content Security Policy directive: "connect-src . Could you have a hint what might I might be doing wrong / not getting?
  • dbakiu over 4 years
    And if you want to directly get a file from the promise, you can generate a file as follows. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
  • waldgeist
    waldgeist almost 4 years
    Unfortunately, this solution does not work for me in Chrome. The browser fails to load this URL.
  • Abhishek Ghosh over 3 years
    I think the OP asked about converting a blob url to actual file/blob, rather than the other way around.
  • Noah Stahl
    Noah Stahl about 3 years
    I was able to use this one-liner to get the result with the blob as data property: const result = await axios.get(url, { responseType: "blob" });
  • therightstuff
    therightstuff about 3 years
    combined with stackoverflow.com/a/18650249/2860309 for reading into base64, this really helped!
  • Matt Fletcher
    Matt Fletcher about 3 years
    Waldgeist, did you wrap it in the createObjectUrl()?
  • Nawin
    Nawin over 2 years
    how can I use this when multiple urls need to pass
  • Nawin
    Nawin over 2 years
    How can we write this as as simple utility function like passing url and getting file anyone
  • mxcl
    mxcl almost 2 years
    There is no “Joe” below.
  • BrianFreud
    BrianFreud almost 2 years
    Good call. If I remember correctly, those URL methods did not exist back when I first asked this question.
  • Kaiido
    Kaiido almost 2 years
    @BrianFreud they still don't exist, we have to implement it ourselves (well I did so now you just have to include that script before URL.createObjectURL is called).
  • yavorbel over 1 year
    let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })). There was a missing bracket at the end
  • Crashalot
    Crashalot about 1 year
    Thanks for this answer! Do you have a Patreon where people can support your work?