前端文件 / 图片核心 API 全解析:File、FileReader、Blob、Base64、URL

一直觉得这些知识点混乱,核心是没理清「数据载体(File/Blob)→ 读取 / 转换工具(FileReader/URL)→ 数据格式(Base64/Blob URL)」的逻辑关系。下面从概念定义、核心 API、使用场景、实操代码四个维度,彻底理清这些知识点。

一、核心概念先理清(避免基础混乱)

先记住一条核心链路:用户选择文件(input/file)→ 得到File对象(继承Blob)→ 用FileReader/URL API处理 → 输出Base64/Blob URL格式 → 用于预览/上传

概念 本质 / 定义 核心特征
File 文件对象,继承自 Blob,代表用户选择的本地文件(如图片 / 文档) 包含文件名、大小、类型、最后修改时间等元信息
Blob 二进制大对象(Binary Large Object),是存储二进制数据的基础对象 仅存二进制数据,无文件元信息(File 是带元信息的 Blob)
FileReader 读取 Blob/File 内容的工具 API(异步) 能将二进制数据转为 Base64 / 文本 / ArrayBuffer
URL API 包含createObjectURL/revokeObjectURL,生成 Blob 的临时 URL(Blob URL) 高效预览大文件,无需读取全部内容
Base64 二进制数据的文本编码格式(将二进制转成 64 个可打印字符) 文本格式,可嵌入 URL/JSON,但体积比原文件大 30%
Blob URL 浏览器为 Blob/File 生成的临时本地 URL(格式:blob:http://xxx 仅本地有效,轻量,适合预览大文件

二、核心 API 详解

1. File 对象(获取用户文件)

(1)定义

File对象是用户通过<input type="file">、拖拽等方式选择的文件,继承自 Blob ,因此拥有 Blob 的所有属性(如size/type),还新增了文件元信息:

  • name:文件名(如avatar.png
  • lastModified:最后修改时间戳
  • lastModifiedDate:最后修改日期(已废弃,用lastModified转 Date)

(2)获取方式(核心场景)

场景 1:通过 input/file 获取单个 / 多个文件

js 复制代码
<input type="file" id="fileInput" multiple>
<script>
  const fileInput = document.getElementById('fileInput');
  fileInput.addEventListener('change', (e) => {
    // e.target.files是FileList(类数组),存储选中的File对象
    const files = e.target.files;
    if (files.length === 0) return;

    // 遍历FileList,获取每个File对象
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      console.log('文件名:', file.name);
      console.log('文件大小:', (file.size / 1024).toFixed(2) + 'KB');
      console.log('文件类型:', file.type); // 如image/png、video/mp4
      console.log('是否是Blob:', file instanceof Blob); // true(File继承Blob)
    }
  });
</script>

场景 2:拖拽获取文件

js 复制代码
<div id="dropArea" style="width: 300px; height: 200px; border: 2px dashed #ccc;">
  拖拽文件到这里
</div>
<script>
  const dropArea = document.getElementById('dropArea');
  // 阻止默认拖拽行为(否则浏览器会直接打开文件)
  ['dragover', 'drop'].forEach(eventName => {
    dropArea.addEventListener(eventName, (e) => e.preventDefault());
  });

  dropArea.addEventListener('drop', (e) => {
    const files = e.dataTransfer.files; // 同样是FileList
    if (files.length) {
      const file = files[0];
      console.log('拖拽的文件:', file.name);
    }
  });
</script>

(3)核心使用场景

  • 作为 FileReader/URL API 的输入源(读取 / 预览文件);
  • 直接通过 FormData 上传到后端(无需转换格式)。

2. Blob 对象(二进制数据容器)

(1)定义

Blob 是「二进制大对象」,是前端存储二进制数据的基础容器,可以理解为:「不管是文件、图片、视频,本质都是二进制数据,Blob 就是装这些数据的盒子」。

(2)核心属性

  • size:二进制数据的大小(字节);
  • type:MIME 类型(如image/jpegtext/plain)。

(3)创建方式(核心场景)

场景 1:从 File 对象获取 Blob(最常用) 因为 File 继承 Blob,所以 File 对象本身就是 Blob,可直接使用:

js 复制代码
fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  const blob = file; // File === 带元信息的Blob
  console.log(blob.size, blob.type);
});

场景 2:手动创建 Blob(如文本转 Blob)

语法:new Blob(blobParts, options)

  • blobParts:数组,包含要放入 Blob 的内容(字符串、ArrayBuffer、Blob 等);
  • options:可选,配置type(MIME)和endings(行结束符)。
js 复制代码
// 示例1:文本转Blob
const text = 'Hello World';
const textBlob = new Blob([text], { type: 'text/plain' });
console.log(textBlob.size); // 11(字符串长度)
console.log(textBlob.type); // text/plain

// 示例2:拼接多个数据为Blob
const part1 = new Blob(['前端'], { type: 'text/plain' });
const part2 = new Blob(['开发'], { type: 'text/plain' });
const combinedBlob = new Blob([part1, part2], { type: 'text/plain' });

场景 3:切割 Blob(Blob.slice) 用于分片上传大文件(如 1GB 文件切成多个 100MB 的 Blob):

js 复制代码
fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  const chunkSize = 1024 * 1024 * 100; // 100MB/片
  const start = 0;
  const end = chunkSize;
  // slice(start, end, contentType):切割Blob
  const chunkBlob = file.slice(start, end, file.type);
  console.log('分片大小:', (chunkBlob.size / 1024 / 1024).toFixed(2) + 'MB');
});

(4)核心使用场景

  • 作为 FileReader 的输入源,读取为 Base64 / 文本;
  • 作为 URL.createObjectURL 的输入源,生成 Blob URL 预览;
  • 分片上传大文件(slice 方法);
  • 前端生成文件(如将文本 / JSON 保存为本地文件)。

3. FileReader(读取 Blob/File 内容的工具)

(1)定义

FileReader 是异步读取 Blob/File 内容的 API(注意:是 "读取",不是 "修改"),可以把二进制的 Blob/File,转换成「人类可读的格式」(Base64、文本、ArrayBuffer)。

(2)核心方法(按使用频率排序)

方法 作用 适用场景
readAsDataURL(blob) 读取为 Base64 编码的 DataURL 图片 / 小文件预览
readAsText(blob) 读取为字符串(默认 UTF-8 编码) 文本 / JSON 文件读取
readAsArrayBuffer() 读取为 ArrayBuffer(原始二进制数据) 处理二进制(如文件加密)
abort() 中断读取 取消文件读取操作

(3)核心事件(异步读取的回调)

  • onload:读取成功完成;
  • onerror:读取失败;
  • onprogress:读取进度(可做上传进度条)。

(4)使用示例(核心场景)

场景 1:图片文件转 Base64(预览小图片)

js 复制代码
<input type="file" id="imgInput" accept="image/*">
<img id="previewImg" style="width: 200px; margin-top: 10px;">
<script>
  const imgInput = document.getElementById('imgInput');
  const previewImg = document.getElementById('previewImg');
  
  imgInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (!file || !file.type.startsWith('image/')) return;

    // 1. 创建FileReader实例
    const reader = new FileReader();
    
    // 2. 监听读取成功事件
    reader.onload = (e) => {
      // e.target.result是Base64字符串(DataURL格式:data:image/png;base64,xxxx)
      previewImg.src = e.target.result;
      console.log('Base64结果:', e.target.result);
    };

    // 3. 监听读取失败
    reader.onerror = () => {
      alert('文件读取失败');
    };

    // 4. 开始读取文件为Base64
    reader.readAsDataURL(file);
  });
</script>

场景 2:读取文本 / JSON 文件内容

js 复制代码
<input type="file" id="textInput" accept=".txt,.json">
<pre id="textPreview"></pre>
<script>
  const textInput = document.getElementById('textInput');
  const textPreview = document.getElementById('textPreview');
  
  textInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = (e) => {
      let content = e.target.result;
      // 如果是JSON文件,格式化展示
      if (file.name.endsWith('.json')) {
        content = JSON.stringify(JSON.parse(content), null, 2);
      }
      textPreview.innerText = content;
    };
    // 读取为文本
    reader.readAsText(file);
  });
</script>

场景 3:监听读取进度(大文件)

js 复制代码
fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  
  // 监听进度(e.loaded已读取字节,e.total总字节)
  reader.onprogress = (e) => {
    const progress = (e.loaded / e.total) * 100;
    console.log(`读取进度:${progress.toFixed(2)}%`);
  };

  reader.onload = () => {
    console.log('读取完成');
  };

  reader.readAsDataURL(file);
});

(5)注意事项

  • FileReader 是异步的,不能用同步方式获取结果(必须等 onload);
  • 读取大文件(如 1GB 视频)时,用 FileReader 会卡顿(推荐用 Blob URL 替代);
  • 读取完成后,若不再需要 reader,建议置为 null 释放内存。

4. URL API(生成 Blob 临时 URL)

(1)核心方法

方法 作用
URL.createObjectURL(blob) 为 Blob/File 生成临时的 Blob URL(格式:blob:http://xxx
URL.revokeObjectURL(blobURL) 释放 Blob URL(避免内存泄漏)

(2)使用场景(核心:高效预览大文件)

场景 1:预览视频 / 音频(大文件优先用这个,比 Base64 快)

js 复制代码
<input type="file" id="mediaInput" accept="video/*,audio/*">
<video id="videoPreview" controls style="width: 300px; margin-top: 10px;"></video>
<audio id="audioPreview" controls style="margin-top: 10px;"></audio>
<script>
  const mediaInput = document.getElementById('mediaInput');
  const videoPreview = document.getElementById('videoPreview');
  const audioPreview = document.getElementById('audioPreview');
  
  mediaInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (!file) return;

    // 1. 生成Blob URL
    const blobURL = URL.createObjectURL(file);
    
    // 2. 预览(视频/音频直接用Blob URL作为src)
    if (file.type.startsWith('video/')) {
      videoPreview.src = blobURL;
      audioPreview.src = '';
    } else if (file.type.startsWith('audio/')) {
      audioPreview.src = blobURL;
      videoPreview.src = '';
    }

    // 3. 页面卸载/文件更换时,释放Blob URL(关键!避免内存泄漏)
    mediaInput.addEventListener('change', () => {
      URL.revokeObjectURL(blobURL);
    });
    window.addEventListener('unload', () => {
      URL.revokeObjectURL(blobURL);
    });
  });
</script>

场景 2:预览图片(小图片 Base64,大图片 Blob URL)

js 复制代码
<input type="file" id="bigImgInput" accept="image/*">
<img id="bigImgPreview" style="width: 300px; margin-top: 10px;">
<script>
  const bigImgInput = document.getElementById('bigImgInput');
  const bigImgPreview = document.getElementById('bigImgPreview');
  
  bigImgInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (!file) return;

    // 判断文件大小:>5MB用Blob URL,否则用Base64
    const maxSize = 5 * 1024 * 1024; // 5MB
    if (file.size > maxSize) {
      const blobURL = URL.createObjectURL(file);
      bigImgPreview.src = blobURL;
      // 释放URL
      bigImgInput.addEventListener('change', () => URL.revokeObjectURL(blobURL));
    } else {
      const reader = new FileReader();
      reader.onload = (e) => {
        bigImgPreview.src = e.target.result;
      };
      reader.readAsDataURL(file);
    }
  });
</script>

(3)Blob URL vs Base64(关键区别)

维度 Blob URL Base64
格式 blob:http://xxx(本地临时 URL) data:image/png;base64,xxxx(文本)
体积 无额外体积(直接指向二进制) 比原文件大 30% 左右
加载速度 快(无需解析文本) 慢(需解析 Base64 编码)
内存占用 低(仅生成 URL,不读取全文件) 高(读取全文件转文本)
使用场景 大文件预览(视频 / 音频 / 大图片) 小图片预览、嵌入 JSON / 本地存储
有效期 浏览器关闭 / 手动 revoke 后失效 永久有效(文本格式)

5. Base64(二进制的文本编码)

(1)定义

Base64 是一种「将二进制数据转换成 64 个可打印字符(A-Z、a-z、0-9、+、/)」的编码方式,核心目的是:让二进制数据能通过 "只支持文本" 的场景传输 / 存储(如 JSON、URL、Cookie)。

(2)核心特征

  • 编码后体积比原二进制大 30%(因为 1 个字节的二进制转成 1.3 个字节的文本);

  • 格式:data:[<mediatype>];base64,<data>(DataURL 格式),其中:

    • mediatype:MIME 类型(如 image/png);
    • <data>:Base64 编码后的文本。

(3)生成 / 解析方式

生成 Base64(两种方式)

① FileReader.readAsDataURL(最常用):

js 复制代码
const reader = new FileReader();
reader.onload = (e) => {
  const base64 = e.target.result; // 完整的DataURL格式
  // 提取纯Base64(去掉前缀)
  const pureBase64 = base64.split(',')[1];
  console.log('纯Base64:', pureBase64);
};
reader.readAsDataURL(file);

② 手动转换(Blob→ArrayBuffer→Base64):

js 复制代码
async function blobToBase64(blob) {
  const arrayBuffer = await new Response(blob).arrayBuffer();
  const uint8Array = new Uint8Array(arrayBuffer);
  let base64 = '';
  // 遍历Uint8Array转Base64(简化版,实际推荐用btoa)
  base64 = btoa(String.fromCharCode(...uint8Array));
  return `data:${blob.type};base64,${base64}`;
}
// 使用
blobToBase64(file).then(base64 => console.log(base64));

解析 Base64 为 Blob(前端还原文件)

场景:后端返回 Base64 图片,前端转成 Blob/File 用于预览 / 上传:

js 复制代码
function base64ToBlob(base64) {
  // 1. 拆分前缀和纯Base64
  const [prefix, pureBase64] = base64.split(',');
  // 2. 解析MIME类型
  const mimeType = prefix.match(/data:(.*?);/)[1];
  // 3. Base64转二进制
  const byteCharacters = atob(pureBase64);
  const byteArrays = [];
  for (let i = 0; i < byteCharacters.length; i++) {
    byteArrays.push(byteCharacters.charCodeAt(i));
  }
  const uint8Array = new Uint8Array(byteArrays);
  // 4. 生成Blob
  return new Blob([uint8Array], { type: mimeType });
}

// 使用示例:Base64转Blob并预览
const base64Str = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...';
const blob = base64ToBlob(base64Str);
const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);

(4)核心使用场景

  • 小图片嵌入 HTML/CSS(如<img src="data:image/png;base64,xxx">);
  • 接口传输二进制数据(如后端只接收 JSON,需把图片转 Base64 放入 JSON);
  • 本地存储(如 localStorage 存小图片,因为 localStorage 只支持文本);
  • 注意:大文件禁止用 Base64(体积大 + 解析慢)。

三、实战综合案例(串联所有知识点)

需求:实现「选择图片→预览(自动选 Blob URL/Base64)→ 转 Blob→ 上传到后端」的完整流程。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>文件API综合案例</title>
  <style>
    .container { max-width: 500px; margin: 20px auto; }
    #preview { width: 100%; margin: 10px 0; }
    #uploadBtn { padding: 8px 16px; background: #409eff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
  </style>
</head>
<body>
  <div class="container">
    <input type="file" id="fileInput" accept="image/*">
    <img id="preview" alt="预览图">
    <button id="uploadBtn" disabled>上传</button>
  </div>

  <script>
    const fileInput = document.getElementById('fileInput');
    const preview = document.getElementById('preview');
    const uploadBtn = document.getElementById('uploadBtn');
    let currentFile = null; // 存储选中的File对象

    // 1. 选择文件后处理
    fileInput.addEventListener('change', async (e) => {
      currentFile = e.target.files[0];
      if (!currentFile) return;

      // 2. 预览(自动选择方式)
      const maxSize = 5 * 1024 * 1024; // 5MB
      if (currentFile.size > maxSize) {
        // 大图片:Blob URL预览
        const blobURL = URL.createObjectURL(currentFile);
        preview.src = blobURL;
        // 释放URL
        fileInput.addEventListener('change', () => URL.revokeObjectURL(blobURL));
      } else {
        // 小图片:Base64预览
        const base64 = await blobToBase64(currentFile);
        preview.src = base64;
      }

      uploadBtn.disabled = false;
    });

    // 3. 上传文件(转Blob后通过FormData上传)
    uploadBtn.addEventListener('click', async () => {
      if (!currentFile) return;

      // File就是Blob,直接放入FormData
      const formData = new FormData();
      formData.append('avatar', currentFile); // key: avatar,value: Blob/File

      try {
        const response = await fetch('/api/upload', {
          method: 'POST',
          body: formData
        });
        const result = await response.json();
        if (result.success) {
          alert('上传成功');
        } else {
          alert('上传失败');
        }
      } catch (err) {
        console.error('上传错误:', err);
        alert('上传出错');
      }
    });

    // 工具函数:Blob转Base64
    function blobToBase64(blob) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (e) => resolve(e.target.result);
        reader.onerror = (err) => reject(err);
        reader.readAsDataURL(blob);
      });
    }
  </script>
</body>
</html>

四、常见误区 & 避坑指南

  • 误区 1:认为 File 和 Blob 是两个完全无关的对象

    ✅ 纠正:File 继承 Blob,File = Blob + 文件元信息(name/lastModified)。

  • 误区 2:预览所有文件都用 Base64

    ✅ 纠正:大文件(视频 / 音频 / 大图片)用 Blob URL,小图片用 Base64。

  • 误区 3:忘记释放 Blob URL

    ✅ 纠正:使用URL.revokeObjectURL释放,否则会导致浏览器内存泄漏。

  • 误区 4:Base64 可以直接上传到后端

    ✅ 纠正:后端接收 Base64 需要额外解析,推荐优先用 FormData 上传 Blob/File(原生二进制,体积更小)。

  • 误区 5:FileReader 是同步的

    ✅ 纠正:FileReader 是异步的,必须在onload事件中获取结果。

最后

记住核心链路:用户选文件(File)→ 作为Blob处理 → 预览(大文件Blob URL/小文件Base64)→ 上传(FormData传Blob/File)

相关推荐
PBitW6 小时前
2025,菜鸟的「Vibe Coding」时刻
前端·年终总结
mwq301236 小时前
不再混淆:导数 (Derivative) 与微分 (Differential) 的本质对决
前端
小二·7 小时前
Vue 3 组件通信全方案详解:Props/Emit、provide/inject、事件总线替代与组合式函数封装
前端·javascript·vue.js
研☆香7 小时前
html框架页面介绍及制作
前端·html
be or not to be8 小时前
CSS 定位机制与图标字体
前端·css
DevUI团队8 小时前
🔥Angular高效开发秘籍:掌握这些新特性,项目交付速度翻倍
前端·typescript·angular.js
Moment8 小时前
如何在前端编辑器中实现像 Ctrl + Z 一样的撤销和重做
前端·javascript·面试
宠..8 小时前
优化文件结构
java·服务器·开发语言·前端·c++·qt
Tencent_TCB8 小时前
AI Coding全流程教程——0基础搭建“MEMO”健康打卡全栈Web应用(附提示词)
前端·人工智能·ai·ai编程·codebuddy·claude code·cloudbase
小猪猪屁8 小时前
权限封装不是写个指令那么简单:一次真实项目的反思
前端·javascript·vue.js