File API(File API)

File API 提供了对文件对象的访问和操作能力,允许 Web 应用读取文件信息、读取文件内容等。

参考规范File API


📚 目录


1. File 对象

1.1 File 对象概述

File 对象是 File API 的核心,表示文件系统中的文件。

继承关系

  • File 继承自 Blob
  • FileBlob 的子类

特点

  • 只读的文件对象
  • 包含文件的元数据(名称、大小、类型、最后修改时间等)
  • 可以通过 FileReaderBlob 方法读取内容

1.2 File 对象属性

const file = input.files[0];
 
// 文件名称
file.name;           // "example.txt"
 
// 文件大小(字节)
file.size;           // 1024
 
// 文件 MIME 类型
file.type;           // "text/plain"
 
// 最后修改时间
file.lastModified;   // 1609459200000 (时间戳)
 
// 最后修改日期
file.lastModifiedDate; // Date 对象(已废弃,但仍可使用)

1.3 File 对象方法

继承自 Blob 的方法

  • slice() - 切片文件
  • stream() - 获取文件流
  • text() - 读取为文本
  • arrayBuffer() - 读取为 ArrayBuffer
  • blob() - 转换为 Blob
const file = input.files[0];
 
// 切片文件
const chunk = file.slice(0, 1024); // 前 1024 字节
 
// 获取文件流
const stream = file.stream();
 
// 读取为文本
const text = await file.text();
 
// 读取为 ArrayBuffer
const buffer = await file.arrayBuffer();

2. FileList 对象

2.1 FileList 概述

FileList 是文件对象的集合,类似于数组但不可直接迭代。

特点

  • 只读的类数组对象
  • 通过索引访问文件
  • 具有 length 属性
  • 可以通过 Array.from() 转换为数组

2.2 FileList 使用

const fileInput = document.getElementById('fileInput');
 
// 获取 FileList
const fileList = fileInput.files;
 
// 获取文件数量
console.log(fileList.length); // 文件数量
 
// 访问文件(通过索引)
const file1 = fileList[0];
const file2 = fileList[1];
 
// 转换为数组
const files = Array.from(fileList);
 
// 遍历文件
for (let i = 0; i < fileList.length; i++) {
  console.log(fileList[i].name);
}
 
// 使用 for...of(需要转换为数组)
for (const file of Array.from(fileList)) {
  console.log(file.name);
}

3. 文件选择

3.1 input 元素选择文件

HTML

<!-- 单选文件 -->
<input type="file" id="fileInput">
 
<!-- 多选文件 -->
<input type="file" id="fileInput" multiple>
 
<!-- 限制文件类型 -->
<input type="file" id="fileInput" accept="image/*">
 
<!-- 限制特定类型 -->
<input type="file" id="fileInput" accept=".jpg,.png,.gif">
 
<!-- 限制 MIME 类型 -->
<input type="file" id="fileInput" accept="image/jpeg,image/png">

JavaScript

const fileInput = document.getElementById('fileInput');
 
// 监听文件选择
fileInput.addEventListener('change', (e) => {
  const files = e.target.files;
  console.log('Selected files:', files);
  
  // 处理文件
  Array.from(files).forEach(file => {
    console.log('File name:', file.name);
    console.log('File size:', file.size);
    console.log('File type:', file.type);
  });
});

3.2 程序化触发文件选择

const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.multiple = true;
fileInput.accept = 'image/*';
 
// 触发文件选择
fileInput.click();
 
fileInput.addEventListener('change', (e) => {
  const files = e.target.files;
  // 处理文件
});

3.3 文件类型限制

// 检查文件类型
function isValidFileType(file, allowedTypes) {
  return allowedTypes.includes(file.type);
}
 
// 检查文件扩展名
function isValidFileExtension(file, allowedExtensions) {
  const extension = file.name.split('.').pop().toLowerCase();
  return allowedExtensions.includes(extension);
}
 
// 使用示例
const file = fileInput.files[0];
if (isValidFileType(file, ['image/jpeg', 'image/png'])) {
  console.log('Valid image file');
}
 
if (isValidFileExtension(file, ['jpg', 'jpeg', 'png', 'gif'])) {
  console.log('Valid image file');
}

3.4 文件大小限制

// 检查文件大小
function isValidFileSize(file, maxSize) {
  return file.size <= maxSize;
}
 
// 使用示例
const file = fileInput.files[0];
const maxSize = 5 * 1024 * 1024; // 5MB
 
if (isValidFileSize(file, maxSize)) {
  console.log('File size is valid');
} else {
  console.log('File is too large');
}

4. 文件拖拽

4.1 拖拽上传区域

HTML

<div id="dropZone" class="drop-zone">
  <p>拖拽文件到此区域上传</p>
</div>

CSS

.drop-zone {
  border: 2px dashed #ccc;
  border-radius: 8px;
  padding: 40px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.3s;
}
 
.drop-zone.dragover {
  border-color: #1890ff;
  background-color: #f0f7ff;
}

JavaScript

const dropZone = document.getElementById('dropZone');
 
// 防止默认行为
dropZone.addEventListener('dragover', (e) => {
  e.preventDefault();
  dropZone.classList.add('dragover');
});
 
dropZone.addEventListener('dragleave', () => {
  dropZone.classList.remove('dragover');
});
 
// 处理文件拖拽
dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  dropZone.classList.remove('dragover');
  
  const files = e.dataTransfer.files;
  console.log('Dropped files:', files);
  
  // 处理文件
  Array.from(files).forEach(file => {
    console.log('File:', file.name);
  });
});

4.2 拖拽事件

const dropZone = document.getElementById('dropZone');
 
// 拖拽进入
dropZone.addEventListener('dragenter', (e) => {
  e.preventDefault();
  console.log('Drag enter');
});
 
// 拖拽悬停
dropZone.addEventListener('dragover', (e) => {
  e.preventDefault();
  console.log('Drag over');
});
 
// 拖拽离开
dropZone.addEventListener('dragleave', (e) => {
  e.preventDefault();
  console.log('Drag leave');
});
 
// 放下文件
dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const files = e.dataTransfer.files;
  console.log('Files dropped:', files);
});

4.3 拖拽数据

dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  
  // 获取文件列表
  const files = e.dataTransfer.files;
  
  // 获取数据类型
  const types = e.dataTransfer.types;
  console.log('Data types:', types);
  
  // 检查是否有文件
  if (types.includes('Files')) {
    console.log('Files dropped');
  }
  
  // 处理文件
  Array.from(files).forEach(file => {
    console.log('File:', file.name, file.size, file.type);
  });
});

5. 文件信息

5.1 获取文件信息

function getFileInfo(file) {
  return {
    name: file.name,
    size: file.size,
    type: file.type,
    lastModified: file.lastModified,
    lastModifiedDate: new Date(file.lastModified),
    extension: file.name.split('.').pop(),
  };
}
 
// 使用示例
const file = fileInput.files[0];
const info = getFileInfo(file);
console.log('File info:', info);

5.2 格式化文件大小

function formatFileSize(bytes) {
  if (bytes === 0) return '0 B';
  
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
 
// 使用示例
const file = fileInput.files[0];
console.log('File size:', formatFileSize(file.size)); // "1.5 MB"

5.3 文件类型检测

function getFileCategory(file) {
  const type = file.type;
  
  if (type.startsWith('image/')) {
    return 'image';
  } else if (type.startsWith('video/')) {
    return 'video';
  } else if (type.startsWith('audio/')) {
    return 'audio';
  } else if (type.startsWith('text/')) {
    return 'text';
  } else if (type.includes('pdf')) {
    return 'pdf';
  } else {
    return 'other';
  }
}
 
// 使用示例
const file = fileInput.files[0];
console.log('File category:', getFileCategory(file));

6. 实际应用

6.1 文件预览

// 图片预览
function previewImage(file) {
  const reader = new FileReader();
  reader.onload = (e) => {
    const img = document.createElement('img');
    img.src = e.target.result;
    document.body.appendChild(img);
  };
  reader.readAsDataURL(file);
}
 
// 文本预览
async function previewText(file) {
  const text = await file.text();
  const pre = document.createElement('pre');
  pre.textContent = text;
  document.body.appendChild(pre);
}

6.2 文件上传

// 使用 FormData 上传
async function uploadFile(file) {
  const formData = new FormData();
  formData.append('file', file);
  
  const response = await fetch('/api/upload', {
    method: 'POST',
    body: formData,
  });
  
  return response.json();
}
 
// 使用 XMLHttpRequest 上传(支持进度)
function uploadFileWithProgress(file, onProgress) {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  formData.append('file', file);
  
  xhr.upload.addEventListener('progress', (e) => {
    if (e.lengthComputable) {
      const percentComplete = (e.loaded / e.total) * 100;
      onProgress?.(percentComplete);
    }
  });
  
  xhr.addEventListener('load', () => {
    if (xhr.status === 200) {
      console.log('Upload successful');
    }
  });
  
  xhr.open('POST', '/api/upload');
  xhr.send(formData);
}

6.3 文件验证

function validateFile(file, options = {}) {
  const {
    maxSize = 10 * 1024 * 1024, // 10MB
    allowedTypes = [],
    allowedExtensions = [],
  } = options;
  
  const errors = [];
  
  // 检查文件大小
  if (file.size > maxSize) {
    errors.push(`File size exceeds ${formatFileSize(maxSize)}`);
  }
  
  // 检查文件类型
  if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
    errors.push('File type not allowed');
  }
  
  // 检查文件扩展名
  if (allowedExtensions.length > 0) {
    const extension = file.name.split('.').pop().toLowerCase();
    if (!allowedExtensions.includes(extension)) {
      errors.push('File extension not allowed');
    }
  }
  
  return {
    valid: errors.length === 0,
    errors,
  };
}
 
// 使用示例
const file = fileInput.files[0];
const validation = validateFile(file, {
  maxSize: 5 * 1024 * 1024, // 5MB
  allowedTypes: ['image/jpeg', 'image/png'],
  allowedExtensions: ['jpg', 'jpeg', 'png'],
});
 
if (validation.valid) {
  console.log('File is valid');
} else {
  console.log('Validation errors:', validation.errors);
}

6.4 多文件处理

// 处理多个文件
async function processFiles(files) {
  const results = [];
  
  for (const file of Array.from(files)) {
    try {
      const result = await processFile(file);
      results.push({ file: file.name, success: true, result });
    } catch (error) {
      results.push({ file: file.name, success: false, error: error.message });
    }
  }
  
  return results;
}
 
// 处理单个文件
async function processFile(file) {
  // 读取文件内容
  const content = await file.text();
  
  // 处理文件内容
  // ...
  
  return { processed: true };
}

🔗 相关链接

相关 API

实际应用


最后更新:2025
参考规范File API


javascript FileAPI 文件操作 浏览器API