如何从对象 URL 获取文件或 blob?

问题描述 投票:0回答:10

我允许用户通过拖放和其他方法将图像加载到页面中。当图像被删除时,我使用

URL.createObjectURL
转换为对象 URL 以显示图像。我不会撤销该网址,因为我会重复使用它。

因此,当需要创建一个

FormData
对象时,以便我可以允许他们上传包含其中一张图像的表单,是否有某种方法可以将该对象 URL 反转回
Blob
File
这样我就可以将其附加到
FormData
对象?

javascript html blob fileapi
10个回答
274
投票

现代解决方案:

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

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


92
投票

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


19
投票

也许有人在使用 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)
         }

    })

11
投票
再次获取 Blob URL 的问题在于,这将创建 Blob 数据的完整“副本”,因此您将在内存中拥有两次,而不是仅在内存中拥有一次。对于大 Blob,这会很快耗尽你的内存使用量。

相当不幸的是,文件 API 不允许我们访问当前链接的 Blob,当然他们认为 Web 作者应该在创建时自行存储该 Blob,这是事实:

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

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

但是对于那些不负责创建 blob:// URI 的人(例如,因为一个库创建了它),我们仍然可以通过覆盖默认的

URL.createObjectURL

URL.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

对象,而在这种情况下获取解决方案只会出错。

使用 fetch 例如如下:

9
投票
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”的文件希望它能有所帮助!
    

参见

5
投票
,其中指出 BlobBuilder 在 Chrome 中不起作用,因此您需要使用:

xhr.responseType = 'arraybuffer';

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

2
投票
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); })

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

2
投票
我有一个使用来自 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 <[email protected]> * */ // 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/

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

按照@Kaiido 的回答,另一种重载 URL 而不弄乱 URL 的方法是扩展 URL 类,如下所示:

2
投票
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 );

打开CHROME,按F12然后打开控制台窗口
然后写

0
投票
var url =“https://images.rawpixel.com/image_800/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIyLTA1L3BmLXMxMjctdGQtMDAyMV80LmpwZw.jpg”

fetch(url).then(res=>res.blob()).then(blob=>{

console.log(blob) var bloburl = URL.createObjectURL(blob) console.log(bloburl) var link = document.createElement("a") link.href = bloburl link.setAttribute('download','image.jpeg') document.body.appendChild(link) link.click()

})

© www.soinside.com 2019 - 2024. All rights reserved.