强制浏览器在点击时下载图像文件

151

我需要浏览器像点击 Excel 表格时一样下载图像文件。

是否有办法仅使用客户端编程来实现这一点?

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <script type="text/javascript" src="Scripts/jquery-1.10.2.js">
        $(document).ready(function () {
            $("*").click(function () {
                $("p").hide();
            });
        });
        </script>
    </head>

    <script type="text/javascript">
        document.onclick = function (e) {
            e = e || window.event;
            var element = e.target || e.srcElement;
            if (element.innerHTML == "Image") {
                //someFunction(element.href);
                var name = element.nameProp;
                var address = element.href;
                saveImageAs1(element.nameProp, element.href);
                return false; // Prevent default action and stop event propagation
            }
            else
                return true;
        };

        function saveImageAs1(name, adress) {
            if (confirm('you wanna save this image?')) {
                window.win = open(adress);
                //response.redirect("~/testpage.html");
                setTimeout('win.document.execCommand("SaveAs")', 100);
                setTimeout('win.close()', 500);
            }
        }
    </script>

    <body>
        <form id="form1" runat="server">
            <div>
                <p>
                    <a href="http://localhost:55298/SaveImage/demo/Sample2.xlsx" target="_blank">Excel</a><br />
                    <a href="http://localhost:55298/SaveImage/demo/abc.jpg" id="abc">Image</a>
                </p>
            </div>
        </form>
    </body>
</html>

在下载Excel表格时应该如何操作(浏览器会怎样)?


9
download 属性。 - Šime Vidas
6
确保文件下载的最佳方法是在服务器端设置内容分发,大多数客户端解决方案并不是那么可靠。 - adeneo
1
可能是重复的问题:https://dev59.com/9HE95IYBdhLWcg3wN7Kv? - Karl-André Gagnon
这里有一个类似的问题已经为您解答了:https://dev59.com/LGw15IYBdhLWcg3wJ4cR#6799284 - dschu
Karl-Andre Gagnon:请仔细查看它。[我没有标记HTML 5,因为我不想使用它],而且不要触及服务器。 - Amit
18个回答

215
使用HTML5,您可以为链接添加属性“download”。
兼容的浏览器将提示下载文件,并以相同的文件名(在此示例中为image.png)保存图像。
如果为此属性指定值,则该值将成为新文件名: 更新:自2018年春季起,对于跨源href,download属性不再可用。因此,如果您想在imgur.com以外的域上创建<a href="https://i.imgur.com/IskAzqA.jpg" download>,它将无法按预期工作。Chrome deprecations and removals announcement

Richard Parnaby-King:使用HTML 5属性可以实现,但我需要在不使用HTML5或任何服务器端工具的情况下完成(如果可能的话)。感谢帮助者 :) - Amit
2
虽然并非所有浏览器都支持,但如果您不关心IE或Safari,则这是一个不错的解决方案。http://caniuse.com/#feat=download - stacigh
谢谢,对于一个HTML5解决方案。如何仍然允许“另存为”,并让我在保存之前输入文件名? - teleman
我不能控制文件下载的位置吗?它直接下载到下载文件夹中。我希望它使用HTML文件的本地路径。 - Arjun Chiddarwar
1
@ArjunChiddarwar 不,那会带来安全漏洞(想象一下有人将恶意文件直接保存到你的Windows文件夹中)。下载路径基于浏览器设置 - 例如,默认情况下 Chrome 将下载到你的下载文件夹。 - Richard Parnaby-King
1
谢谢你提醒我跨域问题。你知道其他浏览器是否也会做同样的事情吗?唉,我讨厌跨域。 - Jeremy S.

137

我成功地在Chrome和Firefox上实现了这个功能,方法是在文档中添加一个链接。

var link = document.createElement('a');
link.href = 'images.jpg';
link.download = 'Download.jpg';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

2
这实际上是一个非常好的解决方案,适用于Javascript无处不在的Web应用程序。但只在Google Chrome中工作(在我的测试设置中)。 - Pavel
16
非常好的解决方案,谢谢!请注意,如果您省略 document.body.appendChild(link),它在 Firefox 中将无法工作。在 link.click() 之后最好使用 document.body.removeChild(link); 删除已创建的元素。 - akn
8
最佳解决方案。为了避免 DOM 中的垃圾,请使用 setTimeout( function () { link.parentNode.removeChild( link ); },10 ); - Mephiztopheles
1
感谢应该被选为最佳答案,因为问题是关于如何在JAVASCRIPT中实现的。 - codehelp4
2
注意1:由于CORS的原因,这无法在本地工作。注意2:在macOS Brave中,这会立即保存图像。在iOS Safari中,这会弹出一个对话框,询问您是否希望查看或保存图像。如果您保存,则它将进入仅在iOS Safari内部可访问的神秘位置(我原以为它会保存到我的“图片”应用程序中,详细信息请参见https://macreports.com/how-to-manage-safari-downloads-on-iphone-and-ipad/)。在iOS Brave上,它只会打开图像。 - 2540625
显示剩余3条评论

59

Leeroy & Richard Parnaby-King:

更新:自2018年春季起,跨域hrefs不再支持此操作。因此,如果您想在除imgur.com外的域上创建,则无法按预期工作。Chrome降级和删除通知

function forceDownload(url, fileName){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.responseType = "blob";
    xhr.onload = function(){
        var urlCreator = window.URL || window.webkitURL;
        var imageUrl = urlCreator.createObjectURL(this.response);
        var tag = document.createElement('a');
        tag.href = imageUrl;
        tag.download = fileName;
        document.body.appendChild(tag);
        tag.click();
        document.body.removeChild(tag);
    }
    xhr.send();
}

2
图片下载了,但是无法打开,因为显示“图片已损坏”。 - mcnk

58

使用Promise和async/await更现代的方法:

async function toDataURL(url) {
    const blob = await fetch(url).then(res => res.blob());
    return URL.createObjectURL(blob);
}

然后
async function download() {
    const a = document.createElement("a");
    a.href = await toDataURL("https://cdn1.iconfinder.com/data/icons/ninja-things-1/1772/ninja-simple-512.png");
    a.download = "myImage.png";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

在此处查找文档:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch


16
如果所请求的图片被CORS策略阻止,这将无法工作。 - Aswath K

23

2018年春季更新

<a href="/path/to/image.jpg" download="FileName.jpg">

虽然这个功能仍然受支持,但从2018年2月起,Chrome已禁用了这个跨域下载功能,这意味着只有在文件位于同一域名下才能正常工作。

我找到了一个解决方案,可以在Chrome的新更新中禁用跨域下载后下载跨域图像。您可以将其修改为函数以适应您的需求。如果需要,您可能还可以通过更多研究来获得图像MIME类型(jpeg、png、gif等)。也许也有一种类似于视频的方法。希望这能帮助到某些人!

Leeroy & Richard Parnaby-King:

更新:截至2018年春季,这已不再适用于跨域hrefs。因此,如果您想在imgur.com以外的域上创建,则无法按预期工作。Chrome deprecations and removals announcement

var image = new Image();
image.crossOrigin = "anonymous";
image.src = "https://is3-ssl.mzstatic.com/image/thumb/Music62/v4/4b/f6/a2/4bf6a267-5a59-be4f-6947-d803849c6a7d/source/200x200bb.jpg";
// get file name - you might need to modify this if your image url doesn't contain a file extension otherwise you can set the file name manually
var fileName = image.src.split(/(\\|\/)/g).pop();
image.onload = function () {
    var canvas = document.createElement('canvas');
    canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
    canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size
    canvas.getContext('2d').drawImage(this, 0, 0);
    var blob;
    // ... get as Data URI
    if (image.src.indexOf(".jpg") > -1) {
    blob = canvas.toDataURL("image/jpeg");
    } else if (image.src.indexOf(".png") > -1) {
    blob = canvas.toDataURL("image/png");
    } else if (image.src.indexOf(".gif") > -1) {
    blob = canvas.toDataURL("image/gif");
    } else {
    blob = canvas.toDataURL("image/png");
    }
    $("body").html("<b>Click image to download.</b><br><a download='" + fileName + "' href='" + blob + "'><img src='" + blob + "'/></a>");
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


1
看起来这段代码将图像解码为浏览器内存中的像素缓冲区,然后重新编码为jpeg/png/gif格式,然后封装到数据URI中,希望能够本地存储。如果是这样,那么意味着图像文件的所有元数据都丢失了。此外,重新编码JPEG会损失一些质量,并且具有更差的质量/字节比率。不过,还是很好知道的。谢谢分享。 - Stéphane Gourichon
1
它下载了图片,但对于我从Firebase存储中获取的图片,它说由于CORS策略被阻止,但不会对你的图片链接产生影响。 - Dipanshu Mahla

14
var pom = document.createElement('a');
pom.setAttribute('href', 'data:application/octet-stream,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
pom.style.display = 'none';
document.body.appendChild(pom);
pom.click();
document.body.removeChild(pom);     

6
Safari浏览器不支持下载属性。 - Pranav Labhe

11

这是一个针对你问题的通用解决方案。但有一个非常重要的部分,即文件扩展名应与您的编码匹配。当然,downlowadImage函数的content参数应该是图像的base64编码字符串。

const clearUrl = url => url.replace(/^data:image\/\w+;base64,/, '');

const downloadImage = (name, content, type) => {
  var link = document.createElement('a');
  link.style = 'position: fixed; left -10000px;';
  link.href = `data:application/octet-stream;base64,${encodeURIComponent(content)}`;
  link.download = /\.\w+/.test(name) ? name : `${name}.${type}`;

  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

['png', 'jpg', 'gif'].forEach(type => {
  var download = document.querySelector(`#${type}`);
  download.addEventListener('click', function() {
    var img = document.querySelector('#img');

    downloadImage('myImage', clearUrl(img.src), type);
  });
});
a gif image: <image id="img" src="data:image/gif;base64,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" />


<button id="png">Download PNG</button>
<button id="jpg">Download JPG</button>
<button id="gif">Download GIF</button>


3
请在您的回答中添加简短的解释,以使其对提问者有意义。 - AGE
1
link.href = 'data:application/octet-stream,' + encodeURIComponent(address) 这会破坏文件,因此无法打开,您能否建议为什么会这样? - OM The Eternity
是的,这个答案值得成为最佳答案!对我来说缺失的部分是“cleanUrl”这个东西,它生成了格式不正确的数据并破坏了文件。 - Iulian Pinzaru
还要注意,如果将包含点的字符串作为第一个参数的值传递给_downloadImage_常量中的函数,则在操作系统(如Windows)上下载的图像将会损坏,因为文件扩展名不会附加到文件名中,因此系统无法识别如何打开该文件。因此,最好将_download字段的值设置为_link_变量中包含的对象,而不使用三元运算符:link.download = \${name}.${type}`;` - zeko868

11
创建一个函数,接收图像URL和文件名,并使用按钮调用该函数。

function downloadImage(url, name){
      fetch(url)
        .then(resp => resp.blob())
        .then(blob => {
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = url;
            // the filename you want
            a.download = name;
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(url);
        })
        .catch(() => alert('An error sorry'));
}
<button onclick="downloadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/1280px-Stack_Overflow_logo.svg.png', 'LogoStackOverflow.png')" >DOWNLOAD</button>

通过JavaScript强制下载Codepen.io中的图像

vladi.cc


7
您可以使用锚点标签直接下载此文件,无需编写太多代码。
将代码片段复制并粘贴到文本编辑器中,然后尝试即可...!

<html>
<head>
</head>
<body>
   <div>
     <img src="https://upload.wikimedia.org/wikipedia/commons/1/1f/SMirC-thumbsup.svg" width="200" height="200">
      <a href="#" download="https://upload.wikimedia.org/wikipedia/commons/1/1f/SMirC-thumbsup.svg"> Download Image </a>
   </div>
</body>
</html>


3
请添加更多细节,例如此代码可以在哪些地方使用。 - Maciej S.
太棒了!这里的href="#"很关键。 - avalanche1
你救了我的一天!太简单了!有很多糟糕的文档甚至没有展示这个... - 29er
4
我尝试了一下,但对我没用。我不明白为什么会这样,因为download属性是用来给元素的标题,而不是路径。 - Raphaël Balet
2
这会为我下载某种HTML内容。 - Kevin Kreps
1
下载属性仅适用于同源URL。 - Crashalot

5
2020年,我使用了Blob来制作图像的本地副本,浏览器将其下载为文件。您可以在此网站上进行测试。

输入图像描述

(function(global) {
  const next = () => document.querySelector('.search-pagination__button-text').click();
  const uuid = () => Math.random().toString(36).substring(7);
  const toBlob = (src) => new Promise((res) => {
    const img = document.createElement('img');
    const c = document.createElement("canvas");
    const ctx = c.getContext("2d");
    img.onload = ({target}) => {
      c.width = target.naturalWidth;
      c.height = target.naturalHeight;
      ctx.drawImage(target, 0, 0);
      c.toBlob((b) => res(b), "image/jpeg", 0.75);
    };
    img.crossOrigin = "";
    img.src = src;
  });
  const save = (blob, name = 'image.png') => {
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.target = '_blank';
    a.download = name;
    a.click();
  };
  global.download = () => document.querySelectorAll('.search-content__gallery-results figure > img[src]').forEach(async ({src}) => save(await toBlob(src), `${uuid()}.png`));
  global.next = () => next();
})(window);

看起来这段代码将图像解码为浏览器内存中的像素缓冲区(画布),然后重新编码为jpeg / png / gif,然后将其封装到数据URI中,希望它被储存在本地。如果是这样,那么这意味着图像文件的所有元数据都丢失了。此外,重新编码JPEG将损失一些质量并具有更差的质量/字节比率。不过,也很好知道这点。感谢分享。 - Stéphane Gourichon
1
代码太繁琐了。不必要的编码/解码和 CPU 使用率。有更优雅的解决方案。而且,如果不是图像而是视频呢?...// - avalanche1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接