头像预览的实现
js代码:
//javascript document
function readFile (){
var file = document.getElementById("file").files[0];//检查是否为图像文件
if(!/image\/\w+/.test(file.type)) {
alert("请确保文件为图像类型");
return false;
}
var reader = new FileReader();
reader.readAsDataURL(file); //将文件以Data URL形式进行读入页面
reader.onload = function(e) {
var result=document.getElementById("result");
result.innerHTML = '<img src="'+this.result+'" alt=""/>' //在页面上显示文件
}
}html代码:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>图片预览</title> <link href="css/style.css" type="text/css" rel="stylesheet"> </head> <body> <div class="mr-cont"> <h2>个人简历</h2> <div> <div class="mess"> <div> <p> <span>姓名:</span> <input type="text"> </p> <p><span>性别:</span> <input type="text"> </p> </div> <div> <p><span>生日:</span> <input type="text"> </p> <p><span>年龄:</span> <input type="text"> </p> </div> <div> <p><span>专业:</span> <input type="text"> </p> <p><span>学历:</span> <input type="text"> </p> </div> </div> <div class="photo" id="result"></div> </div> <div class="expr"> <h3>校内经历和所获荣誉</h3> <div class="border"><textarea cols="84" rows="11"></textarea></div> </div> <div class="expr"> <h3>社会经历</h3> <div><textarea cols="84" rows="11"></textarea></div> </div> <div class="btn"> <input type="button" value="选择头像"> <input type="file" id="file"> <input type="button" value="预览" onClick="readFile ()"> </div> </div> </body> <script type="text/javascript" src="js/js.js"></script> </html>
上面的代码是如何实现选择文件后点击预览按钮,在右侧头像区域显示图片的功能的?
图片预览功能的完整工作流程
这段代码实现的是选择文件后点击预览按钮,在右侧头像区域显示图片的功能。让我一步步拆解它是如何工作的:
一、HTML 结构分析
关键元素有两个:
<!-- 文件选择框,隐藏的,用户看不到 --> <input type="file" id="file"> <!-- 预览按钮,用户点击这个触发预览 --> <input type="button" value="预览" onClick="readFile ()"> <!-- 头像显示区域,id为result,初始为空 --> <div class="photo" id="result"></div>
布局示意图:

二、JavaScript 代码逐行详解
//javascript document
function readFile (){
// 第1步:获取用户选择的文件
var file = document.getElementById("file").files[0];第1步:获取文件对象
document.getElementById("file"):找到隐藏的文件选择框.files[0]:获取用户选择的第一个文件(因为是单选)此时
file变量就是一个 File 对象,包含了文件的所有信息
// 检查是否为图像文件
if(!/image\/\w+/.test(file.type)) {
alert("请确保文件为图像类型");
return false;
}第2步:验证文件类型
/image\/\w+/是一个正则表达式:image\/:匹配 "image/" 字符串\w+:匹配一个或多个字母、数字、下划线整体匹配
image/jpeg、image/png、image/gif等图片 MIME 类型如果
file.type不匹配(即不是图片),弹出警告并退出函数
var reader = new FileReader(); reader.readAsDataURL(file); //将文件以Data URL形式进行读入页面
第3步:创建 FileReader 并读取文件
new FileReader():创建一个文件读取器对象reader.readAsDataURL(file):将文件读取为 Data URL 格式Data URL 的格式:
data:image/jpeg;base64,/9j/4AAQSkZJRg...它把图片的二进制数据转换成 base64 编码的字符串
这个字符串可以直接作为
<img>的src属性使用
reader.onload = function(e) {
var result = document.getElementById("result");
result.innerHTML = '<img src="'+this.result+'" alt=""/>'; //在页面上显示文件
}
}第4步:读取完成后的回调函数
reader.onload:当文件读取完成时自动执行这个函数e是事件对象,但这里直接用this.result获取读取结果document.getElementById("result"):找到头像显示区域result.innerHTML = '<img src="'+this.result+'" alt=""/>':动态创建一个
<img>标签src属性设置为刚才读取的 Data URL(包含图片数据)把这个图片插入到头像区域中显示
三、数据流向图
用户操作 JavaScript处理 页面显示 ───────────────────────────────────────────────────────────────── 1. 点击"选择头像"按钮 ↓ 弹出文件选择对话框 ↓ 2. 选择图片文件 ↓ file 对象生成 ├─ name: "avatar.jpg" ├─ size: 102400 └─ type: "image/jpeg" ↓ 3. 点击"预览"按钮 → 调用 readFile() ↓ 4. FileReader 读取图片 ↓ 转换成 Data URL (base64编码) ↓ 5. reader.onload 触发 → 获取读取结果 ↓ 6. 动态创建 img 标签 src = Data URL ↓ 7. 插入到 #result 元素 ↓ ┌─────────┐ │ 图片 │ │ 显示 │ └─────────┘
四、Data URL 详解
什么是 Data URL?
Data URL 是一种将文件数据直接嵌入到 URL 中的方案,格式为:
data:[<mediatype>][;base64],<data>
例如一个 1x1 像素的红点图片的 Data URL:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==
为什么用 Data URL?
不需要服务器:图片数据都在 URL 里,不依赖网络请求
即时预览:文件一读完就能显示
安全性:不会泄露本地文件路径
Data URL 的缺点
比原始文件大约 30%(base64 编码膨胀)
不适合大文件(会生成很长的字符串)
五、代码的执行时机
顺序很重要!
reader.readAsDataURL(file); // 第1步:开始读取
reader.onload = function() { // 第2步:设置回调函数
// 显示图片
}注意:虽然代码中 onload 写在后面,但实际执行顺序是:
readAsDataURL()开始读取(异步操作)设置
onload回调读取完成后自动调用
onload
如果反过来写:
reader.onload = function() { ... } // 先设置回调
reader.readAsDataURL(file); // 再开始读取这样更合理,但两种写法都能工作。
六、最终效果
执行完成后,HTML 结构变成:
<div class="photo" id="result"> <img src="data:image/jpeg;base64,/9j/4AAQSkZJRg..." alt=""> </div>
视觉上:用户选择的图片就显示在头像区域了!
七、总结
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 用户选择文件 | 通过 <input type="file"> |
| 2 | 获取 File 对象 | file = files[0] |
| 3 | 验证文件类型 | 正则检查是否为图片 |
| 4 | 创建 FileReader | new FileReader() |
| 5 | 读取为 Data URL | readAsDataURL(file) |
| 6 | 读取完成 | onload 回调触发 |
| 7 | 创建 img 标签 | src = this.result |
| 8 | 插入到页面 | result.innerHTML = ... |
一句话总结:通过 FileReader 把用户选择的图片转换成 Data URL,然后动态创建 img 标签插入到头像区域,实现本地图片预览。
'+this.result+'为什么是读取的Data URl结果?
没见这个result和前面的reader变量之间有关联啊?
一、代码回顾
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e) {
var result = document.getElementById("result");
result.innerHTML = '<img src="'+this.result+'" alt=""/>';
}您疑惑的是:this.result 中的 result 看起来和上面的 reader 变量没有直接关联,为什么它能取到读取结果?
二、关键点:this 指向谁?
在这个回调函数中,this 指向的是触发这个事件的源对象——也就是 reader 对象本身!
验证 this 的指向
reader.onload = function(e) {
console.log(this === reader); // true
console.log(this === e.target); // true(e.target 也指向 reader)
console.log(this.result === reader.result); // true
}所以:
this===readerthis.result===reader.result
这就是为什么可以用 this.result 取到读取结果!
三、可视化理解
// 创建一个 reader 对象
var reader = new FileReader();
// 此时 reader 是空对象,还没有 result 属性
reader.readAsDataURL(file);
// 开始读取,reader.result 还是空的
// 设置回调函数
reader.onload = function(e) {
// 当读取完成时,reader.result 已经被赋值
// 这里的 this 就是 reader
console.log(this.result); // 就是 reader.result
}示意图:
四、为什么可以用 this 而不是 reader?
其实用 reader.result 也是完全正确的:
reader.onload = function(e) {
var result = document.getElementById("result");
// 两种写法都可以
result.innerHTML = '<img src="'+reader.result+'" alt=""/>'; // 直接使用 reader
// 或
result.innerHTML = '<img src="'+this.result+'" alt=""/>'; // 使用 this
}两种写法的区别
| 写法 | 优点 | 缺点 |
|---|---|---|
reader.result | 直观,一看就知道来自 reader | 如果外部变量名改变,要同步修改 |
this.result | 灵活,不依赖外部变量名 | 需要理解 this 的指向 |
五、实际执行过程分解
// 第1步:创建 reader
var reader = new FileReader();
// reader = { result: undefined }
// 第2步:开始读取
reader.readAsDataURL(file);
// 开始异步操作,reader.result 仍为空
// 第3步:设置回调函数
reader.onload = function(e) {
// 这个函数现在只是"注册"了,还没执行
// 函数内部的 this 会在执行时确定
}
// 第4步:读取完成(异步)
// 浏览器自动给 reader.result 赋值为 Data URL
// reader.result = "data:image/jpeg;base64,/9j/4AAQ..."
// 第5步:自动调用回调函数
// 调用时把 reader 作为 this 传入
// 所以函数内部的 this 指向 reader
// 第6步:执行函数体
var result = document.getElementById("result");
result.innerHTML = '<img src="'+this.result+'" alt=""/>';
// 这里的 this.result 就是 reader.result,即 Data URL六、一个更直观的类比
想象这个过程就像点外卖:
// 你点外卖(创建 reader)
var 外卖员 = new 外卖员();
// 你下单(开始读取)
外卖员.取餐(餐厅);
// 你告诉外卖员送到后怎么做(设置回调)
外卖员.送达后 = function() {
// 这里的 this 就是外卖员自己
console.log(this.餐品); // 就是外卖员手里的餐品
}
// 外卖员取到餐后(读取完成)
// 自动调用 送达后 函数,this 指向外卖员在这个类比中:
this=== 外卖员this.餐品=== 外卖员手里的餐品(即reader.result)
七、验证方法
您可以自己验证一下:
reader.onload = function(e) {
console.log('this === reader:', this === reader); // true
console.log('this.result === reader.result:', this.result === reader.result); // true
console.log('e.target === reader:', e.target === reader); // true(e.target 也指向 reader)
var result = document.getElementById("result");
// 三种写法都可以
result.innerHTML = '<img src="'+this.result+'" alt=""/>';
// 或
result.innerHTML = '<img src="'+reader.result+'" alt=""/>';
// 或
result.innerHTML = '<img src="'+e.target.result+'" alt=""/>';
}八、总结
| 问题 | 答案 |
|---|---|
this.result 来自哪里? | 来自 reader.result |
为什么 this 指向 reader? | 在回调函数中,this 指向触发事件的对象 |
可以用 reader.result 吗? | 完全可以,两种写法等价 |
| 哪个写法更好? | reader.result 更直观,this.result 更灵活 |
一句话总结:this.result 中的 this 指向 reader 对象,所以 this.result 就是 reader.result,也就是 FileReader 读取文件后存储结果的地方。
