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

因此,当需要创建一个FormData对象时我可以允许他们上传一个包含这些图像的表单,有什么方法可以将对象URL反向到Blob或File中,这样我就可以将它附加到FormData对象中?


当前回答

按照@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 );

其他回答

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

我有一个网站,从Amazon S3/Azure存储中消费图像,在那里我存储了以uniqueidentifier命名的对象:

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

其中一些映像应该从我们的系统界面下载。 为了避免通过我的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将文件保存在用户计算机上

我已经使用filesver .js保存到磁盘下载的文件,如果你需要完成这一点,请使用这个javascript库:

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

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

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

    })

使用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”,希望对大家有所帮助!

再次获取blob URL的问题是,这将创建blob数据的完整副本,因此它将在内存中只有一次,而不是两次。对于大的Blobs,这可以很快地消耗你的内存。

这是相当不幸的,文件API不让我们访问当前链接的Blob,当然他们认为web作者应该在创建时自己存储Blob,这是正确的:

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

如果您担心这会阻止Blob被垃圾收集,那么您是对的,但是Blob:// URL首先也是如此,直到您撤销它。所以持有指向Blob的指针不会改变任何东西。

但是对于那些不负责创建blob:// URI的人(例如,因为它是库创建的),我们仍然可以通过覆盖默认URL来自己填补API漏洞。createObjectURL和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对象,而在这种情况下,获取解决方案只会出错。

按照@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 );