【问题标题】:How to get a file or blob from an object URL?如何从对象 URL 获取文件或 blob?
【发布时间】:2012-08-06 06:01:40
【问题描述】:

我允许用户通过拖放和其他方法将图像加载到页面中。删除图像时,我使用URL.createObjectURL 转换为对象 URL 以显示图像。我不会撤销 url,因为我会重复使用它。

所以,当需要创建一个 FormData 对象时,我可以允许他们上传包含其中一张图像的表单,有没有什么方法可以将该对象 URL 反转回 BlobFile,然后我可以将其附加到FormData 对象?

【问题讨论】:

  • 不要介意前两个 cmets - 您只需将 XMLHttpRequest 发送到 blob URL。
  • 为什么不简单地将原始文件对象存储在某个地方,然后使用对象 URL 显示它们并在表单提交时使用原始文件?
  • @user764754 因为试图获取用户上传的文件的 URL 显示为“C:\fakepath”
  • 这是XY problem。脚本获取对一个或多个文件的引用,因为这些文件被拖放到页面上。您只需要为这些创建 URL,以便为这些创建链接供用户使用(查看他们拖动的内容,fe),但是要在提交表单时包含文件,您需要添加它们一个方式或其他方式 - 无论是从 URL 还是从原始对象中获取。您可以查看DataTransfer 类来重置您的文件输入控件,否则您的表单几乎毫无用处(您可以放弃它以进行脚本辅助提交)。

标签: javascript html fileapi blobs


【解决方案1】:

现代解决方案:

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

url可以是object url,也可以是普通url。

【讨论】:

  • 而如果想直接从promise中获取文件,可以如下生成文件。 let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
  • 不幸的是,这个解决方案在 Chrome 中对我不起作用。浏览器无法加载此 URL。
  • Waldgeist,你把它包装在 createObjectUrl() 中了吗?
  • let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" }))。最后少了一个括号
【解决方案2】:

正如 genkev 在上面的评论中提到的那样,看起来最好/唯一的方法是使用异步 xhr2 调用:

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

更新(2018 年):对于可以安全使用 ES5 的情况,Joe 在下面有一个更简单的基于 ES5 的答案。

【讨论】:

  • 你什么时候会有一个不是当前域和范围本地的 objectURL
  • 我做的和上面一样,但是用 xhr 找不到 404。发生了什么事?
  • @BrianFreud “你什么时候会有一个不在当前域和范围本地的 objectURL?”在我的情况下,我试图给 Amazon S3 blob 一个不同的文件名,它目前是 S3 上的“guid”,没有扩展名。所以,就我而言,我使用的是跨域调用。
  • "对象 URL 是指向磁盘上文件的 URL。"根据定义,ObjectURL 只能是本地的。
  • 下面没有“乔”。
【解决方案3】:

也许有人在使用 React/Node/Axios 时会发现这很有用。我将它用于我的 Cloudinary 图像上传功能,在 UI 上使用 react-dropzone

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

    })

【讨论】:

  • 这对跨域请求有效吗? Twitter 视频有 blob URL。我需要能够收集 blob URL 指向的 blob 对象。我在浏览器中使用 fetch api,这给了我这个错误 - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src 。你能提示一下我可能做错了什么/没有得到什么吗?
  • 我能够使用这个单行来获取带有 blob 作为数据属性的结果: const result = await axios.get(url, { responseType: "blob" });
【解决方案4】:

使用 fetch 示例如下:

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

您可以粘贴到 Chrome 控制台上进行测试。带有“sample.xlsx”下载的文件希望它可以提供帮助!

【讨论】:

  • 我认为 OP 要求将 blob url 转换为实际的文件/blob,而不是相反。
【解决方案5】:

再次获取 Blob URL 的问题在于,这将创建 Blob 数据的完整副本,因此您将拥有两次,而不是只在内存中保存一次。对于大 Blob,这会很快消耗您的内存。

不幸的是,File API 没有让我们访问当前链接的 Blob,当然他们认为网络作者应该在创建时自己存储该 Blob,这是真的:

这里最好存储您在创建 blob:// URL 时使用的对象。

如果您担心这会阻止 Blob 被垃圾收集,那么您是对的,但首先 blob:// URL 也是如此,直到您撤销它。所以给自己一个指向那个 Blob 的指针不会改变任何事情。

但是对于那些不负责创建 blob:// URI 的人(例如,因为库制作了它),我们仍然可以通过覆盖默认的 URL.createObjectURL em> 和 URL.revokeObjectURL 方法,以便它们存储对传递对象的引用。

确保在调用生成 blob:// URI 的代码之前调用此函数。

// 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 ) );

另一个优点是它甚至可以检索MediaSource 对象,而在这种情况下,获取解决方案只会出错。

【讨论】:

  • 好电话。如果我没记错的话,当我第一次问这个问题时,那些 URL 方法并不存在。
  • @BrianFreud 他们仍然不存在,我们必须自己实现它(我这样做了,现在您只需在调用 URL.createObjectURL 之前包含该脚本)。
【解决方案6】:

请参阅Getting BLOB data from XHR request,其中指出 BlobBuilder 在 Chrome 中不起作用,因此您需要使用:

xhr.responseType = 'arraybuffer';

【讨论】:

    【解决方案7】:

    不幸的是,@BrianFreud 的回答不符合我的需要,我有一点不同的需要,我知道这不是@BrianFreud 问题的答案,但我把它留在这里是因为很多人都和我一样需要。我需要诸如“如何从 URL 获取文件或 blob?”之类的内容,而当前的正确答案不符合我的需要,因为它不是跨域的。

    我有一个使用 Amazon S3/Azure 存储中的图像的网站,并在那里存储使用唯一标识符命名的对象:

    示例:http://****.blob.core.windows.net/systemimages/bf142dc9-0185-4aee-a3f4-1e5e95a09bcf

    其中一些图像应从我们的系统界面下载。 为了避免通过我的 HTTP 服务器传递这个流量,因为这个对象不需要任何安全性来访问(通过域过滤除外),我决定在用户​​的浏览器上发出直接请求并使用本地处理来给文件一个真实的名称和扩展名。

    为了实现这一点,我使用了 Henry Algus 的这篇精彩文章: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

    1.第一步:为jquery添加二进制支持

    /**
    *
    * jquery.binarytransport.js
    *
    * @description. jQuery ajax transport for making binary data type requests.
    * @version 1.0 
    * @author Henry Algus <henryalgus@gmail.com>
    *
    */
    
    // use this transport for "binary" data type
    $.ajaxTransport("+binary", function (options, originalOptions, jqXHR) {
        // check for conditions and support for blob / arraybuffer response type
        if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) {
            return {
                // create new XMLHttpRequest
                send: function (headers, callback) {
                    // setup all variables
                    var xhr = new XMLHttpRequest(),
            url = options.url,
            type = options.type,
            async = options.async || true,
            // blob or arraybuffer. Default is blob
            dataType = options.responseType || "blob",
            data = options.data || null,
            username = options.username || null,
            password = options.password || null;
    
                    xhr.addEventListener('load', function () {
                        var data = {};
                        data[options.dataType] = xhr.response;
                        // make callback and send data
                        callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
                    });
    
                    xhr.open(type, url, async, username, password);
    
                    // setup custom headers
                    for (var i in headers) {
                        xhr.setRequestHeader(i, headers[i]);
                    }
    
                    xhr.responseType = dataType;
                    xhr.send(data);
                },
                abort: function () {
                    jqXHR.abort();
                }
            };
        }
    });
    

    2。第二步:使用此传输类型发出请求。

    function downloadArt(url)
    {
        $.ajax(url, {
            dataType: "binary",
            processData: false
        }).done(function (data) {
            // just my logic to name/create files
            var filename = url.substr(url.lastIndexOf('/') + 1) + '.png';
            var blob = new Blob([data], { type: 'image/png' });
    
            saveAs(blob, filename);
        });
    }
    

    现在您可以随意使用创建的 Blob,在我的情况下,我想将其保存到磁盘。

    3.可选:使用 FileSaver 将文件保存在用户的计算机上

    我已经使用 FileSaver.js 将下载的文件保存到磁盘,如果您需要这样做,请使用这个 javascript 库:

    https://github.com/eligrey/FileSaver.js/

    我希望这可以帮助其他有更具体需求的人。

    【讨论】:

    • ObjectURLs 根据定义是本地的。对象 URL 是指向磁盘上文件的 URL。鉴于没有跨域 objectURL 这样的东西,您正在组合两个不同的概念,因此您的问题使用给定的解决方案。
    • @BrianFreud 我知道这不完全是这个问题的答案。但是我仍然认为离开是一个很好的答案,因为我来这里是为了寻找一个不同的问题“如何从 URL 获取文件或 blob?”的答案。如果您检查自己的答案,则有 10 票赞成“它在跨域请求的情况下不起作用。 '。所以有 10 多人来到这里寻找那个。然后我决定把它留在这里。
    • 问题是,你的答案故意没有回答这个问题。普通 URL 和对象 URL 是两个完全不同的东西。关于“它在跨域请求的情况下不起作用。”的赞成票数,您是否认为它不能更好地解释为什么这在此处实际上是不可能的,并指向一个确实显示正常 URL 答案的地方,而不是而不是通过混淆普通 URL 和对象 URL 来混淆这里的东西?
    • @BrianFreud 随时建议对我的答案进行编辑。我会研究这个主题,也许我误解了什么是“objectURL”和“object URL”……英语不是我的母语。我将对该主题进行研究以提供更好的答案。但我认为其他人来这里寻找不同的东西。我没有搜索“objectURL”来到达这里,这是我之前的观点。但我也得到了你。
    【解决方案8】:

    如果您在画布中显示文件,您也可以将画布内容转换为 blob 对象。

    canvas.toBlob(function(my_file){
      //.toBlob is only implemented in > FF18 but there is a polyfill 
      //for other browsers https://github.com/blueimp/JavaScript-Canvas-to-Blob
      var myBlob = (my_file);
    })
    

    【讨论】:

    • 请注意,这实际上并没有给您返回 same 原始文件;它正在制作一个新的图像文件。几年前我最后一次测试时,我发现至少在 Chrome 中,新的图像文件根本没有被压缩——我有 10k jpg 变成 2.5 mb jpg。
    【解决方案9】:

    按照@Kaiido 的回答,另一种重载 URL 而不会弄乱 URL 的方法是像这样扩展 URL 类:

    export class URLwithStore extends URL {
      static createObjectURL(blob) {
        const url = super.createObjectURL(blob);
        URLwithStore.store = { ...(URLwithStore.store ?? {}), [url]: blob };
        return url;
      }
    
      static getFromObjectURL(url) {
        return (URLwithStore.store ?? {})[url] ?? null;
      }
    
      static revokeObjectURL(url) {
        super.revokeObjectURL(url);
        if (
          new URL(url).protocol === "blob:" &&
          URLwithStore.store &&
          url in URLwithStore.store
        )
          delete URLwithStore.store[url];
      }
    }
    

    用法

    const blob = new Blob( ["foo"] );
    const url = URLwithStore.createObjectURL( blob );
    const retrieved = URLwithStore.getFromObjectURL( url );
    console.log( "retrieved Blob is Same Object?", retrieved === blob );
    

    【讨论】:

      猜你喜欢
      • 2020-06-05
      • 2017-10-19
      • 2019-06-15
      • 2021-12-05
      • 2018-12-28
      • 2018-08-07
      • 2016-06-22
      • 2020-01-15
      相关资源
      最近更新 更多