190

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

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

4

9 回答 9

195

现代解决方案:

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

url 可以是对象 url 或普通 url。

于 2018-09-19T16:04:41.453 回答
82

正如 genkev 在他上面的评论中提到的那样,看起来最好/唯一的方法是使用 async 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 的答案。

于 2012-08-10T12:25:51.690 回答
17

也许有人在使用 React/Node/Axios 时发现这很有用。我在 UI 上将它用于我的 Cloudinary 图像上传功能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)
         }

    })
于 2018-02-06T11:44:10.240 回答
9

使用 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”下载的文件希望它可以提供帮助!

于 2019-09-20T04:34:12.443 回答
6

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

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

最好的方法是存储您在创建 blob:// URL 时使用的对象。

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

但是对于那些不负责创建 blob:// URI 的人(例如,因为库制作了它),我们仍然可以通过覆盖默认的URL.createObjectURLURL.revokeObjectURL方法来自己填补这个 API 漏洞,以便他们存储对传递对象的引用。

确保在调用生成 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对象,而在这种情况下,获取解决方案只会出错。

于 2021-04-08T06:29:37.137 回答
5

请参阅从 XHR 请求中获取 BLOB 数据,其中指出 BlobBuilder 在 Chrome 中不起作用,因此您需要使用:

xhr.responseType = 'arraybuffer';
于 2014-12-12T21:58:01.357 回答
3

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

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

示例: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/

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

于 2016-11-22T11:52:47.483 回答
2

如果您在画布中显示文件,您还可以将画布内容转换为 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);
})
于 2014-05-07T16:31:22.423 回答
1

在@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 );
于 2021-10-06T10:51:53.363 回答