gpt4 book ai didi

javascript - 使用chrome.downloads API发送引荐来源 header

转载 作者:行者123 更新时间:2023-11-29 10:45:25 34 4
gpt4 key购买 nike

我正在尝试创建一个扩展,该扩展模仿Ctrl + Click以保存Opera 12中的图像功能。我正在使用扩展的后台脚本中的chrome.downloads.download()来启动下载,而内容脚本则在监听用户操作并发送带有图片网址的消息以进行下载。

一切正常,除了在pixiv.net之类的某些站点上,下载被中断并失败。使用webRequest API,我验证了 Activity 选项卡中的cookie是否与下载请求一起发送,但未发送引用 header 。我希望这是因为该站点阻止了来自外部站点的下载请求。不过,由于无法在失败的下载中触发webRequest.onError事件,因此我无法对此进行验证。

我无法自行设置参照 header ,因为无法通过chrome.downloads进行设置,并且webRequest.onBeforeSendHeaders不能以其阻止形式与下载请求一起使用,因此以后无法添加 header 。有没有一种方法可以在选项卡的上下文中开始下载,使其行为类似于右键单击>另存为...?

为了澄清我是如何开始下载的,这是我的TypeScript代码,其内容简化为适用的部分。

注入(inject)脚本:

window.addEventListener('click', (e) => {
if (suspended) {
return;
}

if (e.ctrlKey && (<Element>e.target).nodeName === 'IMG') {
chrome.runtime.sendMessage({
url: (<HTMLImageElement>e.target).src,
saveAs: true,
});

// Prevent the click from triggering links, etc.
e.preventDefault();
e.stopImmediatePropagation();

// This can trigger for multiple elements, so disable it
// for a moment so we don't download multiple times at once
suspended = true;
window.setTimeout(() => suspended = false, 100);
}
}, true);

后台脚本:
interface DownloadMessage {
url: string;
saveAs: boolean;
}

chrome.runtime.onMessage.addListener((message: DownloadMessage, sender, sendResponse) => {
chrome.downloads.download({
url: message.url,
saveAs: message.saveAs,
});
});

更新

从下面的ExpertSystem解决方案开始,我发现了一种最有效的方法。当后台脚本收到下载图像的请求时,我现在将URL的主机名与用户配置的需要解决方法的站点列表进行比较。如果需要解决方法,我会将消息发送回选项卡。该选项卡使用带有 responseType = 'blob'的XMLHttpRequest下载图像。然后,我在Blob上使用 URL.createObjectURL()并将该URL传递回后台脚本进行下载。这避免了数据URI的大小限制。另外,如果XHR请求失败,我将使用标准方法强制尝试,以便用户至少看到失败的下载出现。

现在的代码如下所示:

注入(inject)脚本:
// ...Original code here...

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.action) {
case 'xhr-download':
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';

xhr.addEventListener('load', (e) => {
chrome.runtime.sendMessage({
url: URL.createObjectURL(xhr.response),
filename: message.url.substr(message.url.lastIndexOf('/') + 1),
saveAs: message.saveAs,
});
});

xhr.addEventListener('error', (e) => {
// The XHR method failed. Force an attempt using the
// downloads API.
chrome.runtime.sendMessage({
url: message.url,
saveAs: message.saveAs,
forceDownload: true,
});
});

xhr.open('get', message.url, true);
xhr.send();
break;
}
});

后台脚本:
interface DownloadMessage {
url: string;
saveAs: boolean;
filename?: string;
forceDownload?: boolean;
}

chrome.runtime.onMessage.addListener((message: DownloadMessage, sender, sendResponse) => {
var downloadOptions = {
url: message.url,
saveAs: message.saveAs,
};

if (message.filename) {
options.filename = message.filename;
}

var a = document.createElement('a');
a.href = message.url;

if (message.forceDownload || a.protocol === 'blob:' || !needsReferralWorkaround(a.hostname)) {
// No workaround needed or the XHR workaround is giving
// us its results.
chrome.downloads.download(downloadOptions);

if (a.protocol === 'blob:') {
// We don't need the blob URL any more. Release it.
URL.revokeObjectUrl(message.url);
}
} else {
// The XHR workaround is needed. Inform the tab.
chrome.tabs.sendMessage(sender.tab.id, {
action: 'xhr-download',
url: message.url,
saveAs: message.saveAs
});
}
});

当然,如果图像与页面不在同一个域中并且服务器未发送CORS header ,则此操作将失败,但我认为不会有太多站点限制引用者下载图像并从中提供图像一个不同的域。

最佳答案

chrome.download.download() 方法的第一个参数(options)可以包含 header 属性,该属性是一个对象数组:

Extra HTTP headers to send with the request if the URL uses the HTTP[s] protocol. Each header is represented as a dictionary containing the keys name and either value or binaryValue, restricted to those allowed by XMLHttpRequest.



更新:

不幸的是,正如您所指出的那样,“限于XMLHttpRequest所允许的那些”部分会有所不同,因为 Referer也不是XHR的允许头。

我在这个问题上花了很多时间,但并没有找到令人满意的解决方案或解决方法。不过我确实很接近,因此如果有人觉得有用,我将在这里展示结果。 (此外,HTML5规范的一些即将推出的增强功能实际上可能使之成为可行的解决方法。)

首先,简便快捷的替代方法(尽管有一个主要的缺点)是通过编程方式创建并“单击”指向图像源URL的链接( <a>元素)。

优点:
  • 真的很容易快速实现。
  • 避免了所有与 header 和CORS相关的问题(稍后会详细介绍),因为它直接发生在网页上下文中。
  • 完全不需要chrome.downloads API。
  • 不需要背景页。

  • 缺点:
  • 无法控制文件的下载位置(尽管您可以指定文件名)以及是否显示文件对话框。

  • 如果您不关心将图像保存在默认的“下载”文件夹中,则可以使用这种方法:)

    实现:
    var suspended = false;
    window.addEventListener('click', function(evt) {
    if (suspended) {
    return;
    }

    if (evt.ctrlKey && (evt.target.nodeName === 'IMG')) {
    var a = document.createElement('a');
    a.href = evt.target.src;
    a.target = '_blank';
    a.download = a.href.substring(a.href.lastIndexOf('/') + 1);
    a.click();

    evt.preventDefault();
    evt.stopImmediatePropagation();

    suspended = true;
    window.setTimeout(function() {
    suspended = false;
    }, 100);
    }
    }, true);

    为了避免上述解决方案的局限性,我尝试了以下过程:
  • 当按住Ctrl键并单击图像时,源URL将发送到背景页面。
  • 后台页面在新选项卡中打开URL(因此选项卡的来源与图片相同-稍后将很重要)。
  • 后台页面将一些代码注入(inject)到新创建的选项卡中:
    一种。创建一个<canvas>元素并将图像绘制到该元素上。
    b。将绘制的图像转换为dataURL。(*)
    C。将dataURL发送回后台页面以进行进一步处理。
  • 后台页面使用chrome.downloads.download()和接收到的dataURL作为url属性的值,接收dataURL,关闭先前打开的选项卡并启动下载。

  • (*):为防止“信息泄漏”,canvas元素不允许将图像转换为dataURL,除非其来源与当前网页位置相同。这就是为什么必须在新选项卡中打开图像的源URL的原因。

    优点:
  • 它允许控制是否显示文件对话框。
  • 它提供了chrome.downloads API提供的所有便利(是否需要是不同的事情)。
  • 它几乎按预期工作:/

  • 缺点-注意事项:
  • 这很慢,因为必须将图像加载到新选项卡中。
  • 可保存图像的最大大小取决于URL允许的最大长度。尽管我找不到关于此的任何“官方”资源,但根据大多数资料似乎都可以看出,几百MB的图像可能在限制范围内(这是个人估计,可能非常不准确)。但是,这是一个限制。
  • 主要限制来自于 Canvas 的toDataURL()以96dpi的分辨率返回数据的事实。因此,尤其是对于高分辨率图像而言,这是一个秀场。
    好消息:它是同级方法toDataURLHD()以本地 Canvas 位图分辨率返回它(数据)。
    不太好消息:Google Chrome当前不支持toDataURLHD()

  • 您可以找到有关 canvas规范及其 toDataURL() / toDataURLHD()方法 here 的更多信息。希望它将很快得到支持,并将此解决方案带回游戏中:)

    实现:

    一个示例扩展名将包含3个文件:
  • manifest.json: list
  • content.js:内容脚本
  • background.js:后台页面

  • manifest.json:
    {
    "manifest_version": 2,

    "name": "Test Extension",
    "version": "0.0",
    "offline_enabled": false,

    "background": {
    "persistent": false,
    "scripts": ["background.js"]
    },

    "content_scripts": [{
    "matches": ["*://*/*"],
    "js": ["content.js"],
    "run_at": "document_idle",
    "all_frames": true
    }],

    "permissions": [
    "downloads",
    "*://*/*"
    ],
    }

    content.js:
    var suspended = false;
    window.addEventListener('click', function(evt) {
    if (suspended) {
    return;
    }

    if (evt.ctrlKey && (evt.target.nodeName === 'IMG')) {

    /* Initialize the "download" process
    * for the specified image's source-URL */
    chrome.runtime.sendMessage({
    action: 'downloadImgStart',
    url: evt.target.src
    });

    evt.preventDefault();
    evt.stopImmediatePropagation();

    suspended = true;
    window.setTimeout(function() {
    suspended = false;
    }, 100);
    }
    }, true);

    background.js:
    /* This function, which will be injected into the tab with the image,
    * takes care of putting the image into a canvas and converting it to a dataURL
    * (which is sent back to the background-page for further processing) */
    var imgToDataURL = function() {
    /* Determine the image's name, type, desired quality etc */
    var src = window.location.href;
    var name = src.substring(src.lastIndexOf('/') + 1);
    var type = /\.jpe?g([?#]|$)/i.test(name) ? 'image/jpeg' : 'image/png';
    var quality = 1.0;

    /* Load the image into a canvas and convert it to a dataURL */
    var img = document.body.querySelector('img');
    var canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    var dataURL = canvas.toDataURL(type, quality);

    /* If the specified type was not supported (falling back to the default
    * 'image/png'), update the `name` accordingly */
    if ((type !== 'image/png') && (dataURL.indexOf('data:image/png') === 0)) {
    name += '.png';
    }

    /* Pass the dataURL and `name` back to the background-page */
    chrome.runtime.sendMessage({
    action: 'downloadImgEnd',
    url: dataURL,
    name: name
    });
    }

    /* An 'Immediatelly-invoked function expression' (IIFE)
    * to be injected into the web-page containing the image */
    var codeStr = '(' + imgToDataURL + ')();';

    /* Listen for messages from the content scripts */
    chrome.runtime.onMessage.addListener(function(msg, sender) {

    /* Require that the message contains a 'URL' */
    if (!msg.url) {
    console.log('Invalid message format: ', msg);
    return;
    }

    switch (msg.action) {
    case 'downloadImgStart':
    /* Request received from the original page:
    * Open the image's source-URL in an unfocused, new tab
    * (to avoid "tainted canvas" errors due to CORS)
    * and inject 'imgToDataURL' */
    chrome.tabs.create({
    url: msg.url,
    active: false
    }, function(tab) {
    chrome.tabs.executeScript(tab.id, {
    code: codeStr,
    runAt: 'document_idle',
    allFrames: false
    });
    });
    break;
    case 'downloadImgEnd':
    /* The "dirty" work is done: We acquired the dataURL !
    * Close the "background" tab and initiate the download */
    chrome.tabs.remove(sender.tab.id);
    chrome.downloads.download({
    url: msg.url,
    filename: msg.name || '',
    saveAs: true
    });
    break;
    default:
    /* Repot invalie message 'action' */
    console.log('Invalid action: ', msg.action, ' (', msg, ')');
    break;
    }
    });


    抱歉,答案很长(这也未完全提出可行的解决方案)。
    我希望有人能在其中找到有用的东西(或者至少可以节省一些时间尝试相同的东西)。

    关于javascript - 使用chrome.downloads API发送引荐来源 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20579112/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com