前端文件下载的方法,你了解多少?

91 阅读3分钟

1.使用HTML的<a>标签

这是最简单也是最直接的方法。只要为<a>标签的herf属性指定文件的URL,并添加download属性。

优点:
  • 最简单直接的方法,易于实现。
  • 不需要JavaScript, 适用静态文件下载。
  • 兼容性好。
缺点:
  • 不能用于非同源URL的下载控制。
  • 无法实现复杂的逻辑处理,如下载前的用户确认或下载后的回调。
注意:

使用<a>标签时,不同的浏览器会有不同的表现(比如直接打开不能强制下载),这跟是否同源以及浏览器的版本有关。

<a href="/path/to/file" download>下载文件</a>

如果给download属性指定值,那么下载文件将使用该值作为文件名(某些浏览器可能不支持)。

2.使用JavaScript动态创建并触发点击事件

当你需要在用户执行某个操作(如点击按钮)后才下载文件,而不是直接展示下载链接时,可以使用javascript来动态创建一个<a>元素,并模拟点击它。

优点:
  • 灵活性高,可以动态指定下载文件的URL和文件名。
  • 可以在用户执行某操作后触发下载,提升用户体验。
缺点:
  • 需要JavaScript支持,对于禁用JavaScript的环境不适用。
  • 某些浏览器可能会阻止或限制此类动态生成和点击的行为。
function downloadFile(url, filename) {
    const a = document.createElement('a');
    a.href = url;
    a.download = filename || 'download';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

3.利用Blob和URL.createObjectURL()

当你需要下载的文件是由前端动态生成的,或者需要通过Ajax请求获取的数据时,可以使用Blob对象和URL.createObjectURL()方法。

优点:
  • 适用于下载前端动态生成的数据或通过Ajax请求得到的文件。
  • 不受同源策略限制,可以处理任意来源的数据。
缺点:
  • 实现相对复杂,需要处理Blob对象。
  • 需要手动管理生成的URL,以避免内存泄漏。
function downmloadBlob(blob, filename){
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
}

4.使用Fetch API或XMLHttpRequest

当需要从服务器下载文件,且处理过程需要更复杂的控制(如进度条显示)时,可以使用Fetch API或者XMLHttpRequest。

优点:
  • 可以处理较为复杂的下载需求,比如进度条显示。
  • 适用于需要前端参与处理响应数据的场景。
缺点:
  • 实现较为复杂,需要处理HTTP请求和响应。
  • 需要考虑跨域问题。
  • 某些旧浏览器不支持Fetch API。
使用Fetch API
function downloadFileWithProgress(url, onProgress) {
    return new Promise((resolve, reject) => {
        fetch(url).then(response=>{
            if(!reponse.ok) {
                reject(new Error('下载失败'));
            }
            
            const contentLength = reponse.headers.get('Content-Length');
            const total = parseInt(contentLength, 10);
            let loaded = 0;
            
            const reader = reponse.body.getReader();
            
            function read(){
                reader.read().then(({done, value})=>{
                    if(done) {
                        resolve();
                        return;
                    }
                    
                    loaded += value.byteLength;
                    onProgress(loaded, total);
                    
                    read();
                }).catch(reject);
            }
            
            read();
        }).catch(reject);
    });
}

// 使用示例
const progressBar = document.getElmentById('progressBar');

downloadFileWithProgress('url-to-download', (loaded, total)=>{
    const percent = (loaded / total) * 100;
    progressBar.style.width = percent + '%';
});
使用XMLHttpRequest
function downloadFileWithProgress(url, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'blob';
        
        xhr.onprogress = (event) => {
            if(event.lengthComputable) {
                const percent = (event.loaded / event.total)*100
                onProgress(event.loaded, event.total)
            }
        };
        
        xhr.onload = () => {
            if(xhr.status === 200) {
                resolve(xhr.response);
            } else {
                reject(new Error('下载失败'));
            }
        };
        
        xhr.onerror = () => {
            reject(new Error('下载失败'));
        };
        
        xhr.send();
    });
}

// 使用示例
const progressBar = document.getElementById('progressBar');

downloadFileWithProgress('url-to-download', (loaded, total) => {
    const percent = (loaded / total) * 100;
    progressbar.style.width = percent + '%';
});

5.利用window.location或iframe

直接通过window.location跳转或创建一个隐藏的iframe来下载文件。这种方法一般用于无需前端参与文件生成,且文件直接通过URL访问的情况。

优点:
  • 实现简单,适用于直接通过URL下载文件的场景。
  • 无需额外的前端逻辑处理。
缺点:
  • 无法处理复杂的下载逻辑,如下载前确认或者处理非直接可下载的内容。
  • 使用iframe可能会遇到浏览器的限制或安全问题。
  • 改变window.location可能会影响当前页面状态或导航历史。
使用window.location
function downloadViaLocation(url){
    window.location.href = url;
}
使用iframe
function downloadViaIframe(url){
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = url;
    document.body.appendChild(iframe);
    setTimeout(()=>document.body.removeChild(iframe), 1000);
}