Link to file download
Basic Principles of File Download
The core of file downloading lies in the Content-Disposition
field in the server's response headers. When the browser receives a response with the attachment
parameter, it triggers a download behavior instead of directly rendering the content. For example:
HTTP/1.1 200 OK
Content-Disposition: attachment; filename="example.pdf"
Content-Type: application/pdf
Basic Download Methods in HTML
The simplest implementation is using the download
attribute of the <a>
tag. This attribute forces the browser to download the target resource instead of navigating to it:
<a href="/files/report.pdf" download="Annual Report.pdf">Download PDF File</a>
Things to note:
- The
download
attribute only works for same-origin URLs - Cross-origin links ignore this attribute
- You can specify the filename after download
Dynamic Download with JavaScript
Programmatically create a hidden <a>
tag to achieve dynamic downloads:
function downloadFile(url, filename) {
const a = document.createElement('a');
a.href = url;
a.download = filename || 'downloaded-file';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
Blob Object Download
When generating content on the frontend, you can create a Blob object to achieve client-side downloads:
const content = 'Hello, World!';
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'example.txt';
a.click();
// Free memory
setTimeout(() => URL.revokeObjectURL(url), 100);
Chunked Download for Large Files
For large files, use streaming downloads:
async function downloadLargeFile(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const contentLength = +response.headers.get('Content-Length');
let receivedLength = 0;
const chunks = [];
while(true) {
const {done, value} = await reader.read();
if(done) break;
chunks.push(value);
receivedLength += value.length;
console.log(`Download progress: ${(receivedLength/contentLength*100).toFixed(1)}%`);
}
const blob = new Blob(chunks);
const downloadUrl = URL.createObjectURL(blob);
// Create an <a> tag to trigger download...
}
Download Progress Display
Monitor download progress using XMLHttpRequest:
const xhr = new XMLHttpRequest();
xhr.open('GET', '/large-file.zip', true);
xhr.responseType = 'blob';
xhr.onprogress = function(e) {
if(e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
progressBar.style.width = percent + '%';
}
};
xhr.onload = function() {
if(this.status === 200) {
const blob = this.response;
// Process the downloaded blob
}
};
xhr.send();
Multi-File Zip Download
Use the JSZip library to achieve multi-file zip downloads:
const zip = new JSZip();
zip.file("hello.txt", "Hello World\n");
zip.file("image.png", imageBlob, {binary: true});
zip.generateAsync({type:"blob"}).then(function(content) {
const url = URL.createObjectURL(content);
const a = document.createElement('a');
a.href = url;
a.download = 'example.zip';
a.click();
});
Server-Pushed Downloads
Combine WebSocket to achieve server-pushed downloads:
const socket = new WebSocket('wss://example.com/download');
socket.onmessage = function(event) {
if(typeof event.data === 'string') {
// Process metadata
const meta = JSON.parse(event.data);
console.log(`Starting download: ${meta.filename}`);
} else {
// Process binary data
chunks.push(event.data);
}
};
// Server example (Node.js)
ws.on('connection', (client) => {
const stream = fs.createReadStream('large-file.bin');
stream.on('data', (chunk) => client.send(chunk));
stream.on('end', () => client.close());
});
Download Security Considerations
Security issues to consider when implementing file downloads:
- Verify user permissions
// Server middleware example
app.get('/download/:file', authenticateUser, (req, res) => {
if(!userHasPermission(req.user, req.params.file)) {
return res.status(403).send('Access denied');
}
res.download(`/secure-files/${req.params.file}`);
});
- Prevent directory traversal attacks
// Secure path handling
const safePath = require('path').join(
'/secure-dir',
req.params.file.replace(/\.\.\//g, '')
);
- Set download rate limits
# Nginx configuration example
location /downloads/ {
limit_rate 200k; # Limit to 200KB/s
}
Browser Compatibility Handling
Compatibility solutions for different browsers:
function forceDownload(blob, filename) {
if(window.navigator.msSaveOrOpenBlob) {
// IE10+
navigator.msSaveBlob(blob, filename);
} else {
// Standard browsers
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
}
Auto-Open After Download
In some scenarios, you may want to automatically open the file after download:
// Only works for same-origin and browser-allowed MIME types
const a = document.createElement('a');
a.href = '/files/document.pdf';
a.target = '_blank';
a.download = 'document.pdf';
a.click();
Resumable Download Implementation
Use the Range
header to achieve resumable downloads:
// Client-side
const resumeByte = getStoredDownloadProgress(filename);
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Range', `bytes=${resumeByte}-`);
xhr.responseType = 'blob';
xhr.onprogress = (e) => {
storeDownloadProgress(filename, resumeByte + e.loaded);
};
Download Timeout Handling
Add timeout control for downloads:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
fetch('/large-file', {
signal: controller.signal
})
.then(response => {
clearTimeout(timeoutId);
// Process response
})
.catch(err => {
if(err.name === 'AbortError') {
console.log('Download timed out');
}
});
Mobile Download Optimization
Special handling for mobile devices:
// Detect iOS WebView
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const isWebView = isIOS && !window.MSStream;
if(isWebView) {
// iOS WebView requires special handling
window.location = 'itms-services://?action=download-manifest&url=...';
} else {
// Standard download process
normalDownload();
}
Download Logging
Client-side download logging solution:
function logDownload(filename, success) {
navigator.sendBeacon('/download-log', JSON.stringify({
file: filename,
timestamp: Date.now(),
status: success ? 'completed' : 'failed',
userAgent: navigator.userAgent
}));
}
// Usage example
fetch('/file.pdf')
.then(() => logDownload('file.pdf', true))
.catch(() => logDownload('file.pdf', false));
Download Button State Management
Optimize user interaction with download button states:
<button id="downloadBtn" data-state="ready">
<span class="ready">Download File</span>
<span class="downloading">Downloading... <progress value="0" max="100"></span>
<span class="completed">Download Complete!</span>
</button>
<style>
[data-state="ready"] .downloading,
[data-state="ready"] .completed,
[data-state="downloading"] .ready,
[data-state="downloading"] .completed,
[data-state="completed"] .ready,
[data-state="completed"] .downloading {
display: none;
}
</style>
<script>
btn.addEventListener('click', () => {
btn.dataset.state = 'downloading';
downloadFile().then(() => {
btn.dataset.state = 'completed';
});
});
</script>
Download Speed Calculation
Calculate and display download speed in real-time:
let lastLoaded = 0;
let lastTime = Date.now();
let speeds = [];
xhr.onprogress = function(e) {
const now = Date.now();
const duration = (now - lastTime) / 1000; // seconds
const loadedDiff = e.loaded - lastLoaded;
if(duration > 0) {
const speed = loadedDiff / duration; // bytes/s
speeds.push(speed);
if(speeds.length > 5) speeds.shift();
const avgSpeed = speeds.reduce((a,b) => a+b, 0) / speeds.length;
speedDisplay.textContent = formatSpeed(avgSpeed);
}
lastLoaded = e.loaded;
lastTime = now;
};
function formatSpeed(bytesPerSec) {
const kb = bytesPerSec / 1024;
return kb > 1024
? `${(kb/1024).toFixed(1)} MB/s`
: `${kb.toFixed(1)} KB/s`;
}
Download Queue System
Implement sequential downloads for multiple files:
class DownloadQueue {
constructor() {
this.queue = [];
this.isDownloading = false;
}
add(file) {
this.queue.push(file);
if(!this.isDownloading) this.next();
}
next() {
if(this.queue.length === 0) {
this.isDownloading = false;
return;
}
this.isDownloading = true;
const file = this.queue.shift();
this.download(file).finally(() => this.next());
}
download(file) {
return new Promise((resolve) => {
console.log(`Starting download: ${file.name}`);
// Actual download logic...
});
}
}
// Usage example
const queue = new DownloadQueue();
queue.add({name: 'file1.pdf', url: '/files/1.pdf'});
queue.add({name: 'file2.pdf', url: '/files/2.pdf'});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:链接到电子邮件(mailto)
下一篇:页面内锚点的创建与使用