欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

js 下载和重命名文件

最编程 2024-07-17 14:11:33
...

背景

当我们将图片,视频,音乐,PDF或者其他文件上传给后端时,后端为了文件名不重复,可能会做一些处理。比如使用日期+时间戳+随机数的方式名命,又或者使用UUID或其他方式生成文件名。这就会导致一个问题。用户下载文件后,不能直观的通过文件名判断文件内容。举个例子:

  • 夜曲 - 周杰伦.mp3 => 6fe3c93a-d3cd-47c6-ac54-11a6978a08cf.mp3
  • xxx简历.pdf => a4e50bfc-63b0-4a19-b0c9-363c7467b2b4.pdf
  • 全家福.jpg => ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg 上述列子还可以通过后缀名区分,但如果你下载的文件都是同一类型的,那...

下载文件并重命名

对于浏览器来说,当你点击的链接是跳转到一张图片,一个视频,一首音乐的时候,浏览器会直接在新标签页打开并预览,如果你想下载这些文件,你需要右键另存为,或者找到下载按钮。对于压缩包,或其他无法预览的文件,则会直接下载。那有没有办法跳过右键另存为,或找下载按钮这些步骤,直接下载呢?大家应该都试过给a标签添加download属性这种方式吧。

image.png

<a href="path/ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg" download="全家福">download</a>

很遗憾,大多数情况下并没有任何效果。那到底该如何实现呢?其实很简单,需要涉及到XMLHttpRequestURL.createObjectURL()

实现

纸上得来终觉浅,绝知此事要躬行。

话不多说,直接上代码:

<button id="btn" type="button">下载</button>

const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
    downloadFile('path/ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg', '全家福');
});

/**
 * 下载文件
 * @param {string} url 
 * @param {string} fileName 
 */
function downloadFile(url, fileName) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    // 响应类型设置为blob
    xhr.responseType = 'blob';
    // 请求成功
    xhr.addEventListener('load', function () {
        if (xhr.status === 200) {
            const a = document.createElement('a');
            a.href = window.URL.createObjectURL(xhr.response);
            a.download = fileName;
            // 将a标签添加到body中是为了更好的兼容性,谷歌浏览器可以不用添加
            document.body.appendChild(a);
            a.click();
            // 移除
            a.remove();
            // 释放url
            window.URL.revokeObjectURL(a.href);
        }
    });
    // 监听下载进度
    xhr.addEventListener('progress', function (e) {
        let percent = Math.trunc(e.loaded / e.total * 100);
    });
    // 错误处理
    xhr.addEventListener('error', function (e) {
        // todo
    });
    // 发送请求
    xhr.send();
}

效果图

GIF 2021-07-02 12-08-38.gif

此外,如果你想将一串字符保存为txt类型的文件,你同样可以使用Blob实现,只需要将Blob构造函数第二个参数的type属性设置为text/plain。适用于保存签名,公钥,私钥等。

/**
 * 保存文件
 * @param {string} str 
 * @param {string} fileName 
 */
function saveAsFile(str, fileName) {
    const blob = new Blob([str], { type: 'text/plain' });
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = fileName;
    // 将a标签添加到body中是为了更好的兼容性,谷歌浏览器可以不用添加
    document.body.appendChild(a);
    a.click();
    // 移除
    a.remove();
    // 释放url
    window.URL.revokeObjectURL(a.href);
}

在线Demo:jsdemo.codeman.top/html/downlo…

存在的问题

当然此方法并不是完美的,存在以下问题。

  1. 非同源存在跨域问题(Demo已设置允许资源跨域)
  2. 会预先加载文件,如果文件比较大,网速较慢,服务器带宽比较低时,等待时间会比较长(所以Demo弄了个进度条)