131

Reference: FileReader.readAsDataURL

Considering the following example:

function previewFile(file) {

  var reader  = new FileReader();

  reader.onloadend = function () {
    console.log(reader.result);
  }
  reader.readAsDataURL(file);
}

It states:

instanceOfFileReader.readAsDataURL(blob);

blob: The Blob or File from which to read.

  1. How can a local file URL like: 'file:///C:/path-to/root.png' be passed to the readAsDataURL()

  2. Is FileReader() available in a Firefox Addon?

10 Answers 10

87

To convert a URL to a Blob for FileReader.readAsDataURL() do this:

var request = new XMLHttpRequest();
request.open('GET', MY_URL, true);
request.responseType = 'blob';
request.onload = function() {
    var reader = new FileReader();
    reader.readAsDataURL(request.response);
    reader.onload =  function(e){
        console.log('DataURL:', e.target.result);
    };
};
request.send();
Sign up to request clarification or add additional context in comments.

2 Comments

this could really benefit from some elaboration, never defined request.response in the snippet. can responseType or FileReader be used on url's already loaded without making a new XMLHttpRequest(); every time?
it seems to work well only on small files. if there is a large mp4 size around 800M, it takes a long time for the browser to respond and play. How could we leverage partial response here?
75

Expanding on Felix Turner s response, here is how I would use this approach with the fetch API.

async function createFile(){
  let response = await fetch('http://127.0.0.1:8080/test.jpg');
  let data = await response.blob();
  let metadata = {
    type: 'image/jpeg'
  };
  let file = new File([data], "test.jpg", metadata);
  // ... do something with the file or return it
}
createFile();

1 Comment

There's something wrong with this method, I get cors error even with everything related to cors allowed.
49

The suggested edit queue is full for @tibor-udvari's excellent fetch answer, so I'll post my suggested edits as a new answer.

This function gets the content type from the header if returned, otherwise falls back on a settable default type.

async function getFileFromUrl(url, name, defaultType = 'image/jpeg'){
  const response = await fetch(url);
  const data = await response.blob();
  return new File([data], name, {
    type: data.type || defaultType,
  });
}

// `await` can only be used in an async body, but showing it here for simplicity.
const file = await getFileFromUrl('https://example.com/image.jpg', 'example.jpg');

Comments

17

Try this I learned this from @nmaier when I was mucking around with converting to ico: Well i dont really understand what array buffer is but it does what we need:

function previewFile(file) {

  var reader  = new FileReader();

  reader.onloadend = function () {
    console.log(reader.result); //this is an ArrayBuffer
  }
  reader.readAsArrayBuffer(file);
}

notice how i just changed your readAsDataURL to readAsArrayBuffer.

Here is the example @nmaier gave me: https://stackoverflow.com/a/24253997/1828637

it has a fiddle

if you want to take this and make a file out of it i would think you would use file-output-stream in the onloadend

3 Comments

@Noitidart what is really happening here flow-wise? We have not passed file to new FileReader then how is it reading? readAsArraysBuffer getting called before reader.onload ?
Hi @piechuckerr - Now I understand ArrayBuffers very well and DOMFile etc. what is happening above is we are passing a DOMFile and it is being turned into an ArrayBuffer. You can't pass anything else to this function I posted above. What are you trying to do? Load a local file? You can do that from addon scope, and from non-addon scope you will have to give the user a <input type=browse and let them browse to it. When the pick it, then it will return a DOMFile which you can pass above. Or if your image is at http...., a sin remote, just xhr it with responseType:arraybuffer.
For firefox use reader.addEventListener("load", () => { ... }). See firefox mdn docs
14

This information is outdated as of now, but cannot be deleted.

  1. You can create File instances just by specifying a path when your code is chrome-privileged:

    new File("/path/to/file");
    

    File is a sub-class of Blob, so all File instances are also valid Blobs. Please note that this requires a platform path, and not a file URL.

  2. Yes, FileReader is available to addons.

File and FileReader are available in all windows. If you want to use them in a non-window scope (like bootstrap.js or a code module), you may use nsIDOMFile/nsIDOMFileReader.

4 Comments

How would I do it from a local file URL (not path), like: file:///storage/emulated/0/Android/data/xxx/cache/1445607245908.jpg
as per developer.mozilla.org/en-US/docs/Web/API/File/File, there ain't any File() ctor that simply takes path.
it is indeed not as simple as that, as File(path) doesn't exist, it's doesn't take only 'path' as argument. It should be something like that instead: File(bits, path)
Using the fetch api: new File([await (await fetch('/path/to/file')).blob()], '/path/to/file')
8

Here is my code using async awaits and promises

const getBlobFromUrl = (myImageUrl) => {
    return new Promise((resolve, reject) => {
        let request = new XMLHttpRequest();
        request.open('GET', myImageUrl, true);
        request.responseType = 'blob';
        request.onload = () => {
            resolve(request.response);
        };
        request.onerror = reject;
        request.send();
    })
}

const getDataFromBlob = (myBlob) => {
    return new Promise((resolve, reject) => {
        let reader = new FileReader();
        reader.onload = () => {
            resolve(reader.result);
        };
        reader.onerror = reject;
        reader.readAsDataURL(myBlob);
    })
}

const convertUrlToImageData = async (myImageUrl) => {
    try {
        let myBlob = await getBlobFromUrl(myImageUrl);
        console.log(myBlob)
        let myImageData = await getDataFromBlob(myBlob);
        console.log(myImageData)
        return myImageData;
    } catch (err) {
        console.log(err);
        return null;
    }
}

export default convertUrlToImageData;

Comments

5

I know this is an expansion off of @tibor-udvari's answer, but for a nicer copy and paste.

async function createFile(url, type){
  if (typeof window === 'undefined') return // make sure we are in the browser
  const response = await fetch(url)
  const data = await response.blob()
  const metadata = {
    type: type || 'video/quicktime'
  }
  return new File([data], url, metadata)
}

Comments

3

For my React projects, I use following function I created. It basically gets imageUrl as input and returns the file. The idea behind is to use fetch API and get the image as blob and convert to File. For the file name, I just use the original image name using substring function.

Important thing is not to forget try/catch in your functions.

export async function imageUrlToFile(imageUrl) {
    try {
        const response = await fetch(imageUrl);
        if (!response.ok) {
            throw new Error(`Failed to fetch image (status ${response.status})`);
        }
        const blob = await response.blob();
        const filename = imageUrl.substring(imageUrl.lastIndexOf('/') + 1);
        const file = new File([blob], filename, { type: blob.type });
        return file;
    } catch (error) {
        console.error('Error:', error);
        throw error;
    }
}

Comments

0

Here's a simplest way to get blob or file object with vanila.js and promise

const fileURL_to_blob = (file_url) => {
  return new Promise((resolve, reject) => {
    let request = new XMLHttpRequest();
    request.open('GET', file_url, true);
    request.responseType = 'blob';
    request.onload = function() {
        var reader = new FileReader();
        reader.readAsDataURL(request.response);
        reader.onload =  function(e){
            //console.log('DataURL:', e.target.result);
            resolve(e.target.result);
        };
    };
    request.onerror=function(e){
      reject(e);
    }
    request.send();
  });
}

Comments

0

I ended up wanting something similar to this but without having to pass the type for the file or the filename, so I made my own example based on @Felix Turner's example. I used the content-disposition header for the filename first in case the file is coming back from an API endpoint, but use the last part of the URL path if that header doesn't exist.

function getFilenameFromContentDisposition(res) {
  let filename = null;

  const disposition = res.headers.get("content-disposition");

  if (disposition?.includes("attachment")) {
    const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
    const matches = filenameRegex.exec(disposition);
    if (matches?.[1]) {
      filename = matches[1].replace(/['"]/g, "");
      // Sometimes the filename comes in a URI encoded format so decode it
      filename = decodeURIComponent(filename);
      // Sometimes the filename starts with UTF-8, remove that
      filename = filename.replace(/^UTF-8/i, "").trim();
    }
  }

  return filename;
}

async function getFileFromLink(url) {
  const fileRes = await fetch(url);
  const blob = await fileRes.blob();

  let fileName = getFilenameFromContentDisposition(fileRes);
  if (!fileName) {
    fileName = url.split("/").pop();
  }

  const file = new File([blob], fileName, {
    type: blob.type,
  });

  return file;
}

If you wanted to make this better, I'd use the content-disposition package on npm for the parsing as the formatting of it can get strange. I'd also probably use the mime package for ensuring that the filename from the URL has a proper file extension based on the returned content-type

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.