文件读取、分割与进度监控
在前端开发中,我们经常需要读取用户选择的文件,并对文件内容进行处理。本文将介绍如何使用 JavaScript 读取文件内容,分割文件,以及监控文件读取进度等进阶操作。
打开文件
要让用户选择文件,可以使用<input>
标签,将type
属性设置为"file"
:
<input type="file" id="fileInput" />
读取文件内容
FileReader 对象
FileReader
对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File
或Blob
对象指定要读取的文件或数据。
创建FileReader
对象:
const reader = new FileReader();
读取文本文件
可以使用FileReader
的readAsText
方法读取文本文件内容:
<input type="file" id="fileInput" />
<script>
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', function(e) {
const reader = new FileReader();
reader.onload = function() {
console.log(reader.result);
}
reader.readAsText(fileInput.files[0]);
});
</script>
当用户选择文件后,会触发change
事件。在事件处理函数中,创建FileReader
对象,并监听onload
事件。当文件读取完成后,可以通过reader.result
获取文件内容。
读取图片文件
使用readAsDataURL
方法可以读取图片文件,result
属性将包含一个data:URL
格式的字符串(base64 编码)以表示所读取图片的内容。
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', function (e) {
const reader = new FileReader();
reader.onload = function () {
const img = new Image();
img.src = reader.result;
img.width = 200;
img.height = 200;
document.body.appendChild(img);
};
reader.readAsDataURL(fileInput.files[0]);
});
读取完成后,创建一个<img>
元素,将其src
属性设置为reader.result
,即可在页面上显示选择的图片。
处理图片灰度化
利用<canvas>
可以对读取的图片进行像素级处理,实现灰度化效果:
<input type="file" id="fileInput" />
<script>
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', function(e) {
const reader = new FileReader();
reader.onload = function() {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg;
data[i + 1] = avg;
data[i + 2] = avg;
}
ctx.putImageData(imgData, 0, 0);
document.body.appendChild(canvas);
}
img.src = reader.result;
}
reader.readAsDataURL(fileInput.files[0]);
});
</script>
首先将图片绘制到<canvas>
上,然后通过getImageData
获取像素数据。遍历每个像素的 RGB 值,将它们的平均值赋给 RGB,即可得到灰度图像。最后使用putImageData
将处理后的像素数据绘制回<canvas>
。
文件分割
有时我们需要分割大文件,逐段读取其内容。可以使用File
对象的slice
方法来实现:
<input type="file" id="files" />
<span class="readBytesButtons">
读取的字节:
<button data-startbyte="0" data-endbyte="4">1-5</button>
<button data-startbyte="5" data-endbyte="14">6-15</button>
<button data-startbyte="6" data-endbyte="7">7-8</button>
<button>整个文件</button>
</span>
<div id="byteRange"></div>
<div id="byteContent"></div>
<script>
const readBytesButtons = document.querySelector('.readBytesButtons');
const inputFiles = document.getElementById('files');
function readBlob(startByte, endByte) {
const files = inputFiles.files;
if (!files.length) {
alert('请选择一个文件');
return;
}
const file = files[0];
const start = parseInt(startByte) || 0;
const stop = parseInt(endByte) || file.size - 1;
const reader = new FileReader();
reader.onloadend = function (e) {
if (e.target.readyState === FileReader.DONE) {
const content = e.target.result;
document.getElementById('byteRange').textContent = `读取的字节:${start + 1} - ${stop + 1} of ${file.size} bytes`;
document.getElementById('byteContent').textContent = content;
}
};
const blob = file.slice(start, stop + 1);
reader.readAsBinaryString(blob);
}
readBytesButtons.addEventListener('click', function (e) {
if (e.target.tagName === 'BUTTON') {
const startByte = e.target.dataset.startbyte;
const endByte = e.target.dataset.endbyte;
readBlob(startByte, endByte);
}
});
</script>
通过为按钮添加data-startbyte
和data-endbyte
属性,指定要读取的字节范围。当点击按钮时,获取这些属性值,并调用readBlob
函数。
在readBlob
函数中,使用file.slice(start, stop + 1)
获取指定字节范围的Blob
对象,然后使用FileReader
读取该Blob
的内容。
监控读取进度
在读取大文件时,我们可能需要实时显示读取进度。FileReader
提供了onprogress
事件,可以用来监控读取进度:
<input type="file" id="files" />
<button onclick="abortRead()">终止读取</button>
<div id="progressBar">
<div class="percent">0%</div>
</div>
<script>
let reader;
const fileInput = document.getElementById('files');
const progressBar = document.getElementById('progressBar');
const percent = progressBar.querySelector('.percent');
fileInput.addEventListener('change', handleFileSelect);
function handleFileSelect(e) {
reader = new FileReader();
reader.onprogress = updateProgress;
reader.onload = function (e) {
percent.style.width = '100%';
percent.textContent = '100%';
};
reader.onerror = handleError;
reader.readAsDataURL(e.target.files[0]);
}
function updateProgress(e) {
if (e.lengthComputable) {
const percentLoaded = Math.round((e.loaded / e.total) * 100);
percent.style.width = percentLoaded + '%';
percent.textContent = percentLoaded + '%';
}
}
function abortRead() {
reader.abort();
console.log('读取已取消');
}
function handleError(e) {
switch (e.target.error.code) {
case e.target.error.NOT_FOUND_ERR:
console.log('文件未找到');
break;
case e.target.error.NOT_READABLE_ERR:
console.log('文件不可读');
break;
case e.target.error.ABORT_ERR:
console.log('读取已取消');
break;
default:
console.log('读取文件时发生错误');
}
}
</script>
在handleFileSelect
函数中,监听FileReader
的onprogress
事件。每当读取进度发生变化时,就会触发该事件,我们可以在updateProgress
函数中更新进度条的宽度和百分比文本。
此外,还可以监听onload
事件,在文件读取完成后将进度设置为 100%。以及监听onerror
事件,处理可能发生的错误。
通过reader.abort()
方法,可以手动终止文件读取操作。