File API允许Web应用读取和处理用户选择的文件。本章将介绍File API的基本概念和使用方法。
<input type="file" id="fileInput" multiple accept="image/*">
<script>
const input = document.getElementById('fileInput');
input.addEventListener('change', (e) => {
const files = e.target.files;
for (const file of files) {
console.log('文件名:', file.name);
console.log('文件类型:', file.type);
console.log('文件大小:', file.size);
console.log('最后修改:', new Date(file.lastModified));
}
});
</script>
const acceptExamples = {
'image/*': '所有图片类型',
'image/png': 'PNG图片',
'.pdf': 'PDF文件',
'audio/*,video/*': '音频和视频',
'.jpg,.jpeg,.png': '指定扩展名',
'image/*,.pdf': '图片和PDF'
};
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
handleFiles(files);
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
});
function handleFiles(files) {
Array.from(files).forEach(file => {
console.log('处理文件:', file.name);
});
}
function readAsText(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsText(file);
});
}
const text = await readAsText(file);
console.log(text);
function readAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
const dataURL = await readAsDataURL(file);
document.getElementById('preview').src = dataURL;
function readAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(file);
});
}
const buffer = await readAsArrayBuffer(file);
const view = new DataView(buffer);
function readFileWithProgress(file, onProgress) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
onProgress?.(percent);
}
};
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
const result = await readFileWithProgress(file, (percent) => {
console.log(`读取进度: ${percent.toFixed(1)}%`);
});
const blob1 = new Blob(['Hello, World!'], { type: 'text/plain' });
const blob2 = new Blob(
['<html><body>Hello</body></html>'],
{ type: 'text/html' }
);
const blob3 = new Blob(
[JSON.stringify({ name: '张三' })],
{ type: 'application/json' }
);
const parts = ['Hello, ', 'World!'];
const blob4 = new Blob(parts, { type: 'text/plain' });
const text = await blob.text();
const buffer = await blob.arrayBuffer();
const stream = blob.stream();
const url = URL.createObjectURL(blob);
const slice = blob.slice(0, 1024, blob.type);
const blob = new Blob(['Hello'], { type: 'text/plain' });
const file = new File([blob], 'hello.txt', {
type: 'text/plain',
lastModified: Date.now()
});
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('name', file.name);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
return response.json();
}
async function uploadMultipleFiles(files) {
const formData = new FormData();
Array.from(files).forEach((file, index) => {
formData.append(`files[${index}]`, file);
});
const response = await fetch('/api/upload/multiple', {
method: 'POST',
body: formData
});
return response.json();
}
function uploadWithProgress(file, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
onProgress?.(percent);
}
};
xhr.onload = () => {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error('上传失败'));
}
};
xhr.onerror = () => reject(new Error('网络错误'));
const formData = new FormData();
formData.append('file', file);
xhr.open('POST', '/api/upload');
xhr.send(formData);
});
}
class ChunkUploader {
constructor(file, options = {}) {
this.file = file;
this.chunkSize = options.chunkSize || 5 * 1024 * 1024;
this.chunks = Math.ceil(file.size / this.chunkSize);
this.currentChunk = 0;
}
async upload(onProgress) {
const uploadId = await this.initUpload();
for (let i = 0; i < this.chunks; i++) {
const start = i * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
const chunk = this.file.slice(start, end);
await this.uploadChunk(uploadId, i, chunk);
this.currentChunk = i + 1;
onProgress?.((this.currentChunk / this.chunks) * 100);
}
return await this.completeUpload(uploadId);
}
async initUpload() {
const response = await fetch('/api/upload/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: this.file.name,
size: this.file.size,
chunks: this.chunks
})
});
const { uploadId } = await response.json();
return uploadId;
}
async uploadChunk(uploadId, index, chunk) {
const formData = new FormData();
formData.append('uploadId', uploadId);
formData.append('index', index);
formData.append('chunk', chunk);
await fetch('/api/upload/chunk', {
method: 'POST',
body: formData
});
}
async completeUpload(uploadId) {
const response = await fetch('/api/upload/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uploadId })
});
return response.json();
}
}
function downloadFile(content, filename, mimeType = 'text/plain') {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
downloadFile('Hello, World!', 'hello.txt');
downloadFile(JSON.stringify(data), 'data.json', 'application/json');
function downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
async function downloadStream(url, onProgress) {
const response = await fetch(url);
const contentLength = response.headers.get('Content-Length');
const reader = response.body.getReader();
const chunks = [];
let receivedLength = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
receivedLength += value.length;
if (contentLength) {
onProgress?.((receivedLength / contentLength) * 100);
}
}
const blob = new Blob(chunks);
return blob;
}
const blob = await downloadStream('/api/download/large-file', (percent) => {
console.log(`下载进度: ${percent.toFixed(1)}%`);
});
downloadBlob(blob, 'large-file.zip');
function previewImage(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement('img');
img.src = e.target.result;
resolve(img);
};
reader.readAsDataURL(file);
});
}
async function previewImages(files) {
const container = document.getElementById('preview-container');
container.innerHTML = '';
for (const file of files) {
if (file.type.startsWith('image/')) {
const img = await previewImage(file);
img.style.maxWidth = '200px';
img.style.margin = '10px';
container.appendChild(img);
}
}
}
async function previewPDF(file) {
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const page = await pdf.getPage(1);
const scale = 1.5;
const viewport = page.getViewport({ scale });
const canvas = document.getElementById('pdf-canvas');
const context = canvas.getContext('2d');
canvas.width = viewport.width;
canvas.height = viewport.height;
await page.render({
canvasContext: context,
viewport: viewport
}).promise;
}
📁 文件处理建议
- 文件大小限制:前端验证文件大小,避免上传过大文件
- 文件类型验证:检查MIME类型和扩展名
- 内存管理:及时释放URL对象和FileReader
- 用户体验:提供进度反馈和错误提示
🔒 安全注意事项
- 不要信任客户端验证,服务端必须再次验证
- 限制上传文件类型和大小
- 处理文件名中的特殊字符
- 防止路径遍历攻击
下一章将探讨 [Web Components基础](file:///e:/db-w.cn/md_data/javascript/81_Web Components基础.md),学习如何创建可复用的自定义组件。