如何在浏览器中通过 POST 请求加载外国图像?

Posted

技术标签:

【中文标题】如何在浏览器中通过 POST 请求加载外国图像?【英文标题】:How to load foreign image via POST request in browser? 【发布时间】:2015-07-25 19:49:03 【问题描述】:

我的 Web 应用程序 (html5 + javascript) 需要显示由国外 Web 服务生成的 PNG 图像。

但是,该网络服务仅支持 POST 请求。 (更准确地说,它确实提供了 GET 请求,但我必须传输大参数,因此 GET URL 变得太长。)

此外,Web 服务与 Web 应用程序具有不同的域,并且不提供正确的 CORS 标头,因此 Ajax (XMLHTTPRequest) 不起作用。

我的 Web 应用程序是否仍然可以通过 POST 请求加载和显示外部图像?

我要求的解决方案不同于我已经熟知的以下讨厌的解决方法

没有设置转换请求的本地代理(并且还规避同源策略) 不使用某个陌生人的远程代理 不使用 Flash 不使用 Java 小程序 不使用特定于操作系统的功能,例如 ActiveX 控件

但是,无法使用 Internet Explorer 的解决方案是可以接受的。即便是 Firefox 或 Chrome 特定的解决方案也值得赞赏。

【问题讨论】:

请记住,同源策略是关于不窃取 cookie 或个人数据,而不是不访问资源。如果它是关于不访问资源代理也不会工作。 服务是否返回文件或图片的网址? @DaveAlperovich:不确定“返回文件”是什么意思,但服务直接返回图像数据。它不会返回指向另一个返回图像的资源的 URL。 我想我会选择一个丑陋的 JSONP 来克服所有其他黑客。或者至少我会先尝试一下。艰难的场景。 @DaveAlperovich:我真的不知道你在说什么,或者想说什么。 【参考方案1】:

可怕的黑客攻击:

向 iframe 提交表单并在 iframe 中显示图像。

(但不要这样做,这听起来像网络服务器旨在避免将图像直接嵌入其他站点。)

【讨论】:

不,Web 服务没有内联问题。只是 GET 请求太大了。我相应地调整了问题。 不设置代理我认为这是唯一可行的解​​决方案 这太棒了!如果图像大小可变,则图像大小可能会成为问题。 非常抱歉。我打算奖励这个答案的赏金,但由于一些不可逆转的错误,它被奖励给了另一个答案。另见:meta.stackexchange.com/questions/257105/…【参考方案2】:

我有一些可能的解决方案...

解决方案 1

如果您的图像小于 25kb,您可以通过 YQL 执行以下操作:select * from data.uri where url="http://jquery.com/jquery-wp-content/themes/jquery/images/logo-jquery@2x.png" 这样您就可以抓取 base64 图像并继续。要通过 YQL 进行 POST,您应该添加类似 and postdata="foo=foo&bar=bar" 的内容,请查看 this article。

警告:这种方法的性能可能不是很好。从最终用户到 YQL 到服务的跃迁,然后一直返回,存在相当多的延迟。还有一些服务器端处理 YQL 对图像进行 base64 编码并提供一些 JSON 响应。

解决方案 2

启用 CORS 或通过其他代理。一旦你这样做了,如果你仍然无法获得 base64 数据,那么你需要做两件事。首先添加一个处理二进制的 jQuery 传输。其次处理二进制 blob 并将其转换为 base64。

这是我找到的jQuery Binary Transport

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

添加传输后,您可以发出任何类型的 AJAX 请求。

$.ajax(
    type: "POST",
    url: 'http://myservice.com/service/v1/somethingsomething',
    dataType: 'binary',
    success: function(imgData) 
        var img = new Image(),
            reader = new window.FileReader();

        reader.readAsDataURL(imgData); 
        reader.onloadend = function() 
            img.src = reader.result
            $('#logo-events').append(img);
        
    
);

读者应使用Blob 并输出base64 版本。当阅读器完成转换/阅读时,它将创建和图像并将其附加到某处。 GET 或 POST 应该不再重要了。

【讨论】:

解决方案 1 建议使用一些 Yahoo 服务器作为代理。我相信这是比设置本地代理更可怕的行为。我调整了我的问题以使这一点更清楚。 -1 表示“解决方案 2”,因为它完全没有抓住重点。我对 CORS 标头没有影响,这在问题中已明确说明。而“通过其他代理”意味着设置代理,这是我所知道的确切解决方案,我正在寻求更好的解决方案。 (另外,在那种情况下,我会简单地使用 IMG 标记并且不会使用 AJAX,所以这个解决方案也被过度设计了。) @vog 仅仅因为答案是否定的并不意味着它应该得到-1。进行 POST 的唯一方法是通过 AJAX 调用。这也是 yql 的目的,它的目的是充当代理。你在问如何绕过同源策略 #1 说 #2 说不要但是如果你仍然需要 POST 这就是方法。 问题的全部意义(以及赏金)是因为它很难。答案“否”是不正确的,请参阅 Quentin 的 iframe 技巧。我正在寻找更多这样的创意。我对试图向我推销我已经知道的答案不感兴趣。【参考方案3】:

我发现了这个相关的问题:Post data to JsonP

我认为它可能适用于你的情况。

基本上,将您的 jsonp 请求发送到您的服务器(同源策略应该不是问题),并将响应加载为 <img>

就像@Quentin 的回答一样,这个 hack 使用(隐藏的)Iframe

【讨论】:

以上是关于如何在浏览器中通过 POST 请求加载外国图像?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Vuex 中通过 POST 请求发送状态数据?

如何在浏览器中通过 Javascript 压缩图像?

如何在 ruby​​ 中通过 SSL 调用 HTTP POST 方法?

在 Android 的 OKhttp 中通过 POST 请求发送 JSON 正文

在Laravel 5中通过ID和所有外国人递归获取记录

如何在 Android 中通过 URL 加载 ImageView? [关闭]