JavaScript文件的操作方法總結(jié)大全
一、file對(duì)象基本屬性
1、獲取文件對(duì)象
// 通過文件輸入框獲取
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
// 通過拖拽獲取
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
});2、file對(duì)象的一些屬性
if (file) {
console.log('文件名:', file.name); // "example.jpg"
console.log('文件大小:', file.size); // 字節(jié)數(shù)
console.log('文件類型:', file.type); // "image/jpeg"
console.log('最后修改時(shí)間:', file.lastModified); // 時(shí)間戳
console.log('文件對(duì)象:', file); // File 對(duì)象本身
}二、文件讀取方法
1、FileReader API
瀏覽器提供的用于異步讀取用戶計(jì)算機(jī)文件內(nèi)容,主要用于通過<input type="file">或拖放操作選擇的文件
(1)核心功能:
- 異步讀取文件(不阻塞主線程);
- 有文本,dataurl,arraybuffer,二進(jìn)制字符串四種讀取格式;
- 通過事件監(jiān)聽處理讀取結(jié)果;
- 只能讀取用戶明確選擇的文件。
(2)使用方法以及讀取方式
// 創(chuàng)建 FileReader 實(shí)例
const reader = new FileReader();
// 事件監(jiān)聽
reader.onload = (event) => {
console.log('讀取完成:', event.target.result);
};
reader.onerror = (event) => {
console.error('讀取失敗:', event.target.error);
};
reader.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
console.log(`讀取進(jìn)度: ${percent}%`);
}
};
三種讀取方法
//readAsText() -----讀取為文本
reader.readAsText(file,encoding="UTF-8");
reader.onloand=function(){
//處理文本內(nèi)容
const text=reader.result;
console.log("文本內(nèi)容:",text);
};
//readAsDataURL() -----讀取為DataURL
reader.readAsDataURL(file) //適合圖片預(yù)覽
reader.onload=function(){
//直接用作圖片src
const img=document.createElement('img');
img.src=reader.result; //data:image/png;base64,iVBOR...
document.body.appendChild(img);
};
//readAsArrayBuffer() -------讀取為ArrayBuffer
reader.readAsArrayBuffer(file);//處理二進(jìn)制數(shù)據(jù)
reader.onload=function(){
const buffer=reader.result;
//處理二進(jìn)制數(shù)據(jù)
const view=new DataView(buffer);
console.log('第一個(gè)字節(jié):',view.getUint8(0));
//轉(zhuǎn)換為其他格式
const blob=new Blob([buffer]);
const text=new TextDecoder().decode(buffer);
};filereader使用事件驅(qū)動(dòng)模型

const reader = new FileReader();
const file =someFile;
//1、開始讀取時(shí)觸發(fā)
reader.onloadstart=function(event){
console.log("開始讀取文件...");
document.getElementById('progress').textContent="讀取中..."
};
//2、讀取過程中觸發(fā)(用于跟蹤進(jìn)度)
reader.onprogress=function (event){
if(event.lengthComputable){
const percent=(event.loaded/event.total)*100;
console.log(`讀取進(jìn)度:${percent.toFixed(2)}`);
document.getElementById('progress').textContent=`${percent.toFixed(2)}`;
}
};
//3、讀取完成時(shí)觸發(fā)
reader.onload=function(event){
console.log('讀取完成!');
const result=event.target.result;
};
//4、讀取失敗觸發(fā)
reader.onerror=function (event){
console.log('讀取錯(cuò)誤',reader.error);
document.getElementById('error').textContent=`${reader.error.name}`;
};
//5、讀取結(jié)束(無論成功與失敗都會(huì)觸發(fā))
reader.onloadend=function(event){
console.log("讀取結(jié)束");
document.getElementById('progress').textContent="完成";
};
//6、讀取中止時(shí)觸發(fā)
reader.onabort = (event) => {
console.log('讀取被中止');
};
reader.readAsText(file);三、文件驗(yàn)證方法
1、基本驗(yàn)證
function validateFile(file) {
// 檢查是否選擇了文件
if (!file) {
return { valid: false, error: '請(qǐng)選擇文件' };
}
// 檢查文件大?。ɡ缦拗茷?MB)
const maxSize = 5 * 1024 * 1024; // 5MB
if (file.size > maxSize) {
return {
valid: false,
error: `文件太大,不能超過 ${maxSize / 1024 / 1024}MB`
};
}
// 檢查文件類型
const allowedTypes = [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'text/plain'
];
if (!allowedTypes.includes(file.type)) {
return { valid: false, error: '不支持的文件類型' };
}
// 檢查文件名長(zhǎng)度
if (file.name.length > 100) {
return { valid: false, error: '文件名過長(zhǎng)' };
}
return { valid: true, error: null };
}2、圖片文件專門驗(yàn)證
function validateImage(file) {
return new Promise((resolve, reject) => {
if (!file.type.startsWith('image/')) {
reject('不是圖片文件');
return;
}
const img = new Image();
const reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result;
img.onload = () => {
// 檢查圖片尺寸
const maxWidth = 1920;
const maxHeight = 1080;
if (img.width > maxWidth || img.height > maxHeight) {
reject(`圖片尺寸過大,最大支持 ${maxWidth}x${maxHeight}`);
} else {
resolve({
width: img.width,
height: img.height,
aspectRatio: img.width / img.height
});
}
};
img.onerror = () => {
reject('圖片加載失敗,可能已損壞');
};
};
reader.readAsDataURL(file);
});
}四、文件處理方法
1、切片上傳(文件過大)
function uploadFileInChunks(file, chunkSize = 1024 * 1024) { // 1MB 分片
const totalChunks = Math.ceil(file.size / chunkSize);
const fileId = Date.now() + '-' + Math.random().toString(36).substr(2);
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const start = chunkIndex * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('fileId', fileId);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('chunk', chunk);
formData.append('fileName', file.name);
uploadChunk(formData, chunkIndex + 1, totalChunks);
}
}
async function uploadChunk(formData, current, total) {
try {
const response = await fetch('/api/upload-chunk', {
method: 'POST',
body: formData
});
console.log(`上傳分片 ${current}/${total}`);
updateProgress(current, total);
return response.json();
} catch (error) {
console.error('分片上傳失敗:', error);
throw error;
}
}2、壓縮處理
// 圖片壓縮
function compressImage(file, quality = 0.8, maxWidth = 800) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const img = new Image();
reader.onload = (e) => {
img.src = e.target.result;
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 計(jì)算新尺寸
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height = (height * maxWidth) / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
// 繪制并壓縮
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(
(blob) => {
const compressedFile = new File(
[blob],
file.name,
{ type: file.type }
);
resolve(compressedFile);
},
file.type,
quality
);
};
img.onerror = () => reject('圖片加載失敗');
};
reader.onerror = () => reject('文件讀取失敗');
reader.readAsDataURL(file);
});
}五、文件的核心API
1、Blob對(duì)象
是JavaScript表示原始二進(jìn)制數(shù)據(jù)的對(duì)象,它可以存儲(chǔ)文本,圖像,音頻,視頻等各類型的數(shù)據(jù)。
blob的特點(diǎn):
- 是一個(gè)二進(jìn)制容器可以存儲(chǔ)任何類型的數(shù)據(jù)(就像是一個(gè)數(shù)據(jù)快遞箱可以將任何原始數(shù)據(jù)打包成一個(gè)整體,并且為該快遞箱打上標(biāo)簽);
- 創(chuàng)建后內(nèi)容不能修改(但可切片創(chuàng)建新的);
- 創(chuàng)建時(shí)通常只是創(chuàng)建對(duì)原始數(shù)據(jù)的引用,不會(huì)立即復(fù)制數(shù)據(jù),只有實(shí)際讀取時(shí)才處理;
- 可用于文件處理,數(shù)據(jù)存儲(chǔ),網(wǎng)絡(luò)傳輸?shù)取?/li>
(1)基本創(chuàng)建方式
// 方法1:使用 Blob 構(gòu)造函數(shù)
const blob1 = new Blob(['文本內(nèi)容'], {
type: 'text/plain' // MIME 類型
});
// 方法2:從字符串創(chuàng)建
const text = '這是一段文本';
const blob2 = new Blob([text], { type: 'text/plain;charset=utf-8' });
// 方法3:從數(shù)組緩沖區(qū)創(chuàng)建
const buffer = new ArrayBuffer(8);
const view = new Int32Array(buffer);
view[0] = 42;
view[1] = 100;
const blob3 = new Blob([buffer], { type: 'application/octet-stream' });
// 方法4:混合不同類型的數(shù)據(jù)
const blob4 = new Blob([
'文本部分\n',
new Uint8Array([65, 66, 67]), // ABC
'\n結(jié)束'
], { type: 'text/plain' });(2)Blob的核心屬性
const blob = new Blob(['Hello, World!'], {
type: 'text/plain',
endings: 'transparent' // 可選:native, transparent
});
console.log('=== Blob 屬性 ===');
console.log('大小(字節(jié)):', blob.size); // 13
console.log('MIME 類型:', blob.type); // "text/plain"
console.log('是否已關(guān)閉:', blob.isClosed); // false(非標(biāo)準(zhǔn))
console.log('對(duì)象本身:', blob); // Blob {size: 13, type: "text/plain"}(3)核心方法
對(duì)于Blob能使用分片功能說明它是由頭部以及數(shù)據(jù)部分組成的(由應(yīng)用層實(shí)現(xiàn)),blob的分片頭部:

并且在每個(gè)分片上傳后都有HTTP響應(yīng);所有分片上傳后,服務(wù)器驗(yàn)證并確認(rèn)。
//slice() 切片
const blob = new Blob(['0123456789ABCDEF'], { type: 'text/plain' });
// 切片使用方式
const slice1 = blob.slice(); // 整個(gè) Blob 的副本
const slice2 = blob.slice(0, 5); // 前5字節(jié)
const slice3 = blob.slice(5, 10); // 5-10字節(jié)
const slice4 = blob.slice(-5); // 最后5字節(jié)
const slice5 = blob.slice(0, -5); // 除了最后5字節(jié)
const slice6 = blob.slice(5, 10, 'text/plain'); // 指定新類型
console.log('切片示例:');
console.log('原始大小:', blob.size); // 16
console.log('slice2 大小:', slice2.size); // 5
console.log('slice2 類型:', slice2.type); // "text/plain"
//stream() 獲取流
async function processBlobStream(blob) {
// 創(chuàng)建可讀流
const stream = blob.stream();
const reader = stream.getReader();
let totalBytes = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
totalBytes += value.length;
console.log(`讀取了 ${value.length} 字節(jié),累計(jì) ${totalBytes}`);
}
reader.releaseLock();
console.log('流處理完成');
}
// 使用
const blob = new Blob(['a'.repeat(1000)]);
processBlobStream(blob);
//arrayBufer() 轉(zhuǎn)換為 ArrayBuffer
async function blobToArrayBuffer(blob) {
const arrayBuffer = await blob.arrayBuffer();
console.log('ArrayBuffer 大小:', arrayBuffer.byteLength);
// 可以進(jìn)一步處理
const uint8Array = new Uint8Array(arrayBuffer);
const dataView = new DataView(arrayBuffer);
return arrayBuffer;
}
//text() 讀取為文本
async function blobToText(blob) {
const text = await blob.text();
console.log('文本內(nèi)容:', text);
return text;
}
// 處理不同編碼
async function blobToTextWithEncoding(blob, encoding = 'utf-8') {
const arrayBuffer = await blob.arrayBuffer();
const decoder = new TextDecoder(encoding);
return decoder.decode(arrayBuffer);
}(4)Blob URL與Data URL
- data url:內(nèi)容直接包含在url中,自然包含,不需要額外文件;

- blob url:只是一個(gè)引用/指針

兩者區(qū)別:

// 創(chuàng)建 Blob URL
function createBlobURL(blob) {
const blobURL = URL.createObjectURL(blob);
console.log('創(chuàng)建的 Blob URL:', blobURL);
// 格式:blob:http://example.com/uuid
return blobURL;
}
// 使用示例
const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' });
const blobURL = createBlobURL(textBlob);
// 可以像普通 URL 一樣使用
const iframe = document.createElement('iframe');
iframe.src = blobURL;
document.body.appendChild(iframe);
// 或者用于圖片
const imageBlob = someImageBlob;
const imageURL = URL.createObjectURL(imageBlob);
const img = new Image();
img.src = imageURL;
document.body.appendChild(img);
// 使用完畢后一定要釋放!
setTimeout(() => {
URL.revokeObjectURL(blobURL);
URL.revokeObjectURL(imageURL);
console.log('URL 已釋放');
}, 1000);
//data url
// Blob 轉(zhuǎn) Data URL
async function blobToDataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
// Data URL 轉(zhuǎn) Blob
function dataURLToBlob(dataURL) {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
// 比較兩種 URL
async function compareURLs(blob) {
const blobURL = URL.createObjectURL(blob);
const dataURL = await blobToDataURL(blob);
console.log('=== URL 比較 ===');
console.log('Blob URL 長(zhǎng)度:', blobURL.length); // 較短
console.log('Data URL 長(zhǎng)度:', dataURL.length); // 較長(zhǎng)(base64編碼)
console.log('Blob URL 前綴:', blobURL.substring(0, 20));
console.log('Data URL 前綴:', dataURL.substring(0, 50));
// 釋放資源
URL.revokeObjectURL(blobURL);
return { blobURL, dataURL };
}(5)轉(zhuǎn)換矩陣
class BlobConverter {
// Blob → ArrayBuffer
static async toArrayBuffer(blob) {
return await blob.arrayBuffer();
}
// Blob → String
static async toString(blob, encoding = 'utf-8') {
return await blob.text();
}
// Blob → Base64
static async toBase64(blob) {
const dataURL = await this.toDataURL(blob);
return dataURL.split(',')[1]; // 去掉前綴
}
// Blob → Data URL
static async toDataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
// Blob → Object URL
static toObjectURL(blob) {
return URL.createObjectURL(blob);
}
// String → Blob
static fromString(str, type = 'text/plain') {
return new Blob([str], { type });
}
// Base64 → Blob
static fromBase64(base64, type = '') {
const byteString = atob(base64);
const arrayBuffer = new ArrayBuffer(byteString.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], { type });
}
// ArrayBuffer → Blob
static fromArrayBuffer(buffer, type = 'application/octet-stream') {
return new Blob([buffer], { type });
}
// JSON → Blob
static fromJSON(data, type = 'application/json') {
return new Blob([JSON.stringify(data)], { type });
}
// FormData → Blob(模擬表單提交)
static fromFormData(formData) {
return new Blob([new URLSearchParams(formData).toString()], {
type: 'application/x-www-form-urlencoded'
});
}
}2、File API
file對(duì)象是特殊的blob對(duì)象,表示用戶選擇的文件。

(1)file對(duì)象結(jié)構(gòu)
// File 對(duì)象結(jié)構(gòu)
const file = {
name: "example.jpg", // 文件名
size: 102400, // 文件大小(字節(jié))
type: "image/jpeg", // MIME 類型
lastModified: 1672502400000, // 最后修改時(shí)間戳
lastModifiedDate: Date, // 最后修改日期對(duì)象
webkitRelativePath: "" // 相對(duì)路徑(webkit瀏覽器)
};(2)獲取file對(duì)象的方式
文本輸入框方式
<input type="file" id="fileInput">
// 獲取單個(gè)文件
const fileInput = document.getElementById('fileInput');
const singleFile = fileInput.files[0];
// 獲取多個(gè)文件
//<input type="file" id="multiFileInput" multiple>
const multiFileInput = document.getElementById('multiFileInput');
const fileList = multiFileInput.files; // FileList 對(duì)象
const filesArray = Array.from(fileList); // 轉(zhuǎn)為數(shù)組
// 監(jiān)聽文件變化
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
console.log('選擇的文件:', file);
});拖拽獲取
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
// 獲取拖放的文件
const files = e.dataTransfer.files;
if (files.length > 0) {
const file = files[0];
console.log('拖放的文件:', file);
}
});
// 阻止默認(rèn)拖放行為
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
});
});文件選擇器api
async function selectFile() {
try {
const [fileHandle] = await window.showOpenFilePicker({
types: [
{
description: 'Images',
accept: { 'image/*': ['.png', '.jpg', '.jpeg', '.gif'] }
}
],
multiple: false
});
const file = await fileHandle.getFile();
return file;
} catch (err) {
if (err.name !== 'AbortError') {
console.error('選擇文件失敗:', err);
}
}
}(3)核心屬性
function inspectFile(file) {
console.log('=== 文件信息 ===');
console.log('文件名:', file.name); // "example.jpg"
console.log('文件擴(kuò)展名:', file.name.split('.').pop()); // "jpg"
console.log('文件大小:', file.size, '字節(jié)'); // 102400
console.log('格式化大小:', formatFileSize(file.size)); // "100 KB"
console.log('MIME類型:', file.type); // "image/jpeg"
console.log('最后修改時(shí)間戳:', file.lastModified); // 1672502400000
console.log('最后修改日期:', new Date(file.lastModified));
// 瀏覽器特定屬性
console.log('相對(duì)路徑:', file.webkitRelativePath || 'N/A');
console.log('是目錄?', file instanceof File ? '是' : '否');
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}(4)繼承自Blob的方法
// File 繼承自 Blob,所以可以使用 Blob 的所有方法
const file = someFile;
// 1. slice() - 切片文件
const chunk1 = file.slice(0, 1024); // 前1024字節(jié)
const chunk2 = file.slice(1024, 2048); // 1024-2048字節(jié)
const chunk3 = file.slice(2048); // 從2048字節(jié)到最后
// 2. stream() - 獲取文件流(現(xiàn)代瀏覽器)
async function processFileStream(file) {
const stream = file.stream();
const reader = stream.getReader();
let totalBytes = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
totalBytes += value.length;
console.log(`已讀取: ${totalBytes}/${file.size} 字節(jié)`);
}
}
// 3. arrayBuffer() - 異步獲取 ArrayBuffer
async function getFileBuffer(file) {
const arrayBuffer = await file.arrayBuffer();
console.log('ArrayBuffer 大小:', arrayBuffer.byteLength);
return arrayBuffer;
}
// 4. text() - 異步讀取為文本
async function readFileAsText(file) {
try {
const text = await file.text();
console.log('文件內(nèi)容:', text);
return text;
} catch (error) {
console.error('讀取失敗:', error);
}
}
// 5. 一次性獲取所有轉(zhuǎn)換
async function processFile(file) {
// 并行獲取不同格式
const [text, arrayBuffer] = await Promise.all([
file.text(),
file.arrayBuffer()
]);
return { text, arrayBuffer };
}3、FileList API
filelist是一個(gè)類數(shù)組對(duì)象,它是file對(duì)象的集合。

filelist的不可變性:
- filelist只讀,不能直接修改;
- 不能添加,刪除,修改文件;
- 只能通過重新選擇文件來改變。
(1)獲取FileList對(duì)象
//1、從文件輸入框獲取
// 單個(gè)文件選擇
//<input type="file" id="singleFile">
const singleInput = document.getElementById('singleFile');
singleInput.addEventListener('change', (e) => {
const fileList = e.target.files; // 即使單選,返回的也是 FileList
console.log('文件數(shù)量:', fileList.length); // 0 或 1
console.log('第一個(gè)文件:', fileList[0]);
});
// 多個(gè)文件選擇
//<input type="file" multiple id="multiFile">
const multiInput = document.getElementById('multiFile');
multiInput.addEventListener('change', (e) => {
const fileList = e.target.files;
console.log('選擇了', fileList.length, '個(gè)文件');
});
//2、從拖放操作獲取
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
// 從 DataTransfer 獲取 FileList
const fileList = e.dataTransfer.files;
console.log('拖放了', fileList.length, '個(gè)文件');
handleFiles(fileList);
});
// 必須阻止默認(rèn)行為才能正常工作
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
//3、從datatransfer獲取
// 在粘貼事件中獲取
document.addEventListener('paste', (e) => {
const items = e.clipboardData.items;
const files = [];
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'file') {
const file = items[i].getAsFile();
files.push(file);
}
}
// 注意:需要自己創(chuàng)建類 FileList 的對(duì)象
const fileList = {
length: files.length,
item: (index) => files[index],
[Symbol.iterator]: function*() {
for (let i = 0; i < this.length; i++) {
yield this[i];
}
}
};
// 添加索引訪問
files.forEach((file, index) => {
fileList[index] = file;
});
return fileList;
});(2)核心屬性
const fileList = input.files;
// length - 文件數(shù)量
console.log('文件數(shù)量:', fileList.length);
// 索引訪問 - 類數(shù)組特性
console.log('第一個(gè)文件:', fileList[0]);
console.log('第二個(gè)文件:', fileList[1]);
// 遍歷所有屬性
console.log('=== FileList 屬性 ===');
for (let key in fileList) {
if (!isNaN(parseInt(key))) {
console.log(`索引 ${key}:`, fileList[key].name);
}
}(3)核心方法
// item(index) - 通過索引獲取文件
const firstFile = fileList.item(0);
const secondFile = fileList.item(1);
const invalidFile = fileList.item(999); // 返回 null
console.log('item(0):', firstFile);
console.log('item(1):', secondFile);
console.log('item(999):', invalidFile); // null
// 等價(jià)于 fileList[0],但更安全
if (fileList.item(0)) {
console.log('第一個(gè)文件存在');
}(4)遍歷FileList的方法
a、傳統(tǒng)for循環(huán)
function processFilesTraditional(fileList) {
for (let i = 0; i < fileList.length; i++) {
const file = fileList[i];
console.log(`文件 ${i + 1}: ${file.name} (${file.size} 字節(jié))`);
}
}
// 使用 item() 方法
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
console.log(file.name);
}b、for...of循環(huán)
function processFilesForOf(fileList) {
let index = 1;
for (const file of fileList) {
console.log(`文件 ${index}: ${file.name}`);
index++;
}
}
// 注意:FileList 是可迭代的,但不是數(shù)組
console.log(Symbol.iterator in fileList); // true,支持迭代c、Array.from轉(zhuǎn)換為數(shù)組
// 轉(zhuǎn)換為真正的數(shù)組
const filesArray = Array.from(fileList);
// 現(xiàn)在可以使用數(shù)組的所有方法
filesArray.forEach((file, index) => {
console.log(`文件 ${index + 1}: ${file.name}`);
});
// 數(shù)組方法示例
const totalSize = filesArray.reduce((sum, file) => sum + file.size, 0);
const imageFiles = filesArray.filter(file => file.type.startsWith('image/'));
const fileNames = filesArray.map(file => file.name);d、擴(kuò)展運(yùn)算符
// 使用擴(kuò)展運(yùn)算符轉(zhuǎn)為數(shù)組
const files = [...fileList];
// 直接使用數(shù)組方法
[...fileList].forEach((file, index) => {
console.log(`文件 ${index + 1}:`, file.name);
});
// 統(tǒng)計(jì)信息
const stats = {
totalFiles: [...fileList].length,
totalSize: [...fileList].reduce((sum, file) => sum + file.size, 0),
imageCount: [...fileList].filter(f => f.type.startsWith('image/')).length
};e、forEach方法
// FileList 沒有原生的 forEach 方法
// ? 錯(cuò)誤:fileList.forEach(...) // TypeError
// ? 正確:先轉(zhuǎn)換為數(shù)組
Array.from(fileList).forEach((file, index) => {
console.log(`文件 ${index + 1}: ${file.name}`);
});
// 或者創(chuàng)建輔助函數(shù)
function forEachFile(fileList, callback) {
for (let i = 0; i < fileList.length; i++) {
callback(fileList[i], i, fileList);
}
}
// 使用輔助函數(shù)
forEachFile(fileList, (file, index) => {
console.log(`處理文件 ${index}: ${file.name}`);
});(5)操作和修改FileList
a、添加文件到FileList
// FileList 是只讀的,但可以通過 DataTransfer 創(chuàng)建新的
function addFileToInput(fileInput, newFile) {
const dt = new DataTransfer();
// 1. 添加現(xiàn)有的文件
const existingFiles = fileInput.files;
for (let i = 0; i < existingFiles.length; i++) {
dt.items.add(existingFiles[i]);
}
// 2. 添加新文件
dt.items.add(newFile);
// 3. 更新 file input 的 files 屬性
fileInput.files = dt.files;
// 4. 觸發(fā) change 事件
fileInput.dispatchEvent(new Event('change'));
}
// 創(chuàng)建文件并添加
function createAndAddFile(fileInput, content, filename, type = 'text/plain') {
const blob = new Blob([content], { type });
const file = new File([blob], filename, {
type,
lastModified: Date.now()
});
addFileToInput(fileInput, file);
}b、刪除FileList文件
function removeFileFromInput(fileInput, indexToRemove) {
const dt = new DataTransfer();
const files = fileInput.files;
// 復(fù)制除了要?jiǎng)h除的文件外的所有文件
for (let i = 0; i < files.length; i++) {
if (i !== indexToRemove) {
dt.items.add(files[i]);
}
}
// 更新 files
fileInput.files = dt.files;
// 觸發(fā)事件
fileInput.dispatchEvent(new Event('change'));
return dt.files; // 返回新的 FileList
}
// 刪除多個(gè)文件
function removeFilesFromInput(fileInput, indexesToRemove) {
const dt = new DataTransfer();
const files = fileInput.files;
const removeSet = new Set(indexesToRemove);
for (let i = 0; i < files.length; i++) {
if (!removeSet.has(i)) {
dt.items.add(files[i]);
}
}
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
return dt.files;
}c、重新排序FileList
function reorderFileList(fileInput, newOrder) {
const dt = new DataTransfer();
const files = fileInput.files;
// 按照新順序添加文件
newOrder.forEach(index => {
if (index >= 0 && index < files.length) {
dt.items.add(files[index]);
}
});
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
return dt.files;
}
// 移動(dòng)文件位置
function moveFile(fileInput, fromIndex, toIndex) {
const files = Array.from(fileInput.files);
if (fromIndex < 0 || fromIndex >= files.length ||
toIndex < 0 || toIndex >= files.length) {
return fileInput.files;
}
// 移動(dòng)數(shù)組元素
const [movedFile] = files.splice(fromIndex, 1);
files.splice(toIndex, 0, movedFile);
// 更新 FileList
const dt = new DataTransfer();
files.forEach(file => dt.items.add(file));
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
return dt.files;
}4、showOpenFilePicker API
用于讓用戶通過系統(tǒng)原生的文件選擇器選擇一個(gè)或多個(gè)本地文件,并獲取對(duì)這些文件的訪問權(quán)限。
特點(diǎn):
- 他返回一個(gè)promise,需要使用async/await或.then()處理結(jié)果;
- 訪問文件完全由用戶主動(dòng)選擇觸發(fā),頁面不能靜默讀取整個(gè)硬盤,權(quán)限與瀏覽器標(biāo)簽頁綁定;
- 他不直接返回文件file對(duì)象,而是返回FileSystemFileHandle句柄(數(shù)組),通過該句柄進(jìn)一步操作文件;
(1)簡(jiǎn)單使用
const fileHandles = await window.showOpenFilePicker(options);
multiple參數(shù):確定是單選模式還是多選模式
types參數(shù):數(shù)組,用于允許選擇的文件類型
(2)讀取單個(gè)文件
async function readTextFile() {
try {
const [handle] = await showOpenFilePicker({
types: [{
description: '文本文件',
accept: {'text/plain': ['.txt']}
}]
});
const file = await handle.getFile();
const content = await file.text();
// 在頁面顯示內(nèi)容
document.getElementById('content').textContent = content;
// 返回句柄以便后續(xù)操作
return handle;
} catch (err) {
if (err.name !== 'AbortError') {
alert('讀取文件失敗: ' + err.message);
}
}
}(3)權(quán)限狀態(tài)檢測(cè)
async function checkPermission(handle) {
const options = { mode: 'read' };
const status = await handle.queryPermission(options);
if (status === 'granted') {
return true;
} else if (status === 'prompt') {
// 請(qǐng)求權(quán)限
return await handle.requestPermission(options) === 'granted';
}
return false;
}(4)與<input type='file'>對(duì)比

5、showSaveFilePicker API
用于讓用戶選擇保存位置并獲取寫入文件的權(quán)限。
特點(diǎn):
- 返回一個(gè)filesystemfilehandle對(duì)象,允許向指定位置寫入內(nèi)容;
- 獲取的是寫權(quán)限,權(quán)限可持久化,返回promise。
(1)基本使用
// 保存文本內(nèi)容
async function saveText() {
try {
// 1. 顯示保存對(duì)話框
const handle = await window.showSaveFilePicker({
suggestedName: 'document.txt', // 建議的文件名
types: [{
description: '文本文件',
accept: { 'text/plain': ['.txt'] }
}]
});
// 2. 創(chuàng)建可寫流
const writable = await handle.createWritable();
// 3. 寫入內(nèi)容
await writable.write('Hello, World!');
// 4. 關(guān)閉流
await writable.close();
console.log('文件保存成功!');
return handle;
} catch (err) {
if (err.name !== 'AbortError') {
console.error('保存失敗:', err);
}
return null;
}
}(2)配置選項(xiàng)
const options = {
// 建議的文件名(用戶可以修改)
suggestedName: '未命名文檔.txt',
// 默認(rèn)開始位置(如"文檔"、"下載"等文件夾)
startIn: 'downloads', // 或 'desktop', 'documents'
// 文件類型選項(xiàng)
types: [
{
description: '文本文檔', // 用戶看到的描述
accept: { // MIME類型映射
'text/plain': ['.txt', '.text'],
'text/html': ['.html', '.htm']
}
},
{
description: '圖片文件',
accept: {
'image/png': ['.png'],
'image/jpeg': ['.jpg', '.jpeg']
}
}
],
// 是否排除"所有文件"選項(xiàng)
excludeAcceptAllOption: false
};(3)基本寫入流程
async function saveContent(content, filename, mimeType = 'text/plain') {
const handle = await showSaveFilePicker({
suggestedName: filename,
types: [{
description: '文件',
accept: { [mimeType]: [getExtension(mimeType)] }
}]
});
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
return handle;
}
// 使用示例
await saveContent(JSON.stringify(data, null, 2), 'config.json', 'application/json');(4)檢查寫權(quán)限
async function checkWritePermission(handle) {
const options = { mode: 'readwrite' };
const status = await handle.queryPermission(options);
switch (status) {
case 'granted':
return true;
case 'prompt':
// 請(qǐng)求權(quán)限
return await handle.requestPermission(options) === 'granted';
case 'denied':
console.warn('寫權(quán)限被拒絕');
return false;
default:
return false;
}
}
// 使用權(quán)限檢查
async function writeToFile(handle, content) {
const hasPermission = await checkWritePermission(handle);
if (hasPermission) {
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
return true;
}
return false;
}(5)與其配合的“另存為”功能
let currentFileHandle = null;
async function openFile() {
const [handle] = await showOpenFilePicker();
const file = await handle.getFile();
const content = await file.text();
// 保存句柄以便后續(xù)保存
currentFileHandle = handle;
return content;
}
async function saveFile(content) {
if (currentFileHandle) {
// 直接保存到原文件
const writable = await currentFileHandle.createWritable();
await writable.write(content);
await writable.close();
} else {
// 另存為新文件
currentFileHandle = await showSaveFilePicker({
suggestedName: '文檔.txt'
});
const writable = await currentFileHandle.createWritable();
await writable.write(content);
await writable.close();
}
}
async function saveAs(content) {
// 總是顯示"另存為"對(duì)話框
const handle = await showSaveFilePicker({
suggestedName: '文檔.txt'
});
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
// 更新當(dāng)前文件句柄
currentFileHandle = handle;
}6、showDirectoryPicker API
是File System Access API的一部分,它允許網(wǎng)頁安全地訪問用戶本地文件系統(tǒng)的目錄(像在資源管理器中一樣,打開整個(gè)文件夾)。
注:showdirectorypicker只有在HTTPS中可用
與傳統(tǒng)文件上傳對(duì)比:
- 傳統(tǒng)文件上傳:每次只能存/取一個(gè)文件,想要獲取其他的文件,則需重新打開;
- showdirectorypicker:可以獲得整個(gè)訪問權(quán)限,可自由查看,可一次授權(quán)多測(cè)使用。
(1)基本用法
簡(jiǎn)單調(diào)用:

參數(shù)配置:


(2)核心對(duì)象和方法

權(quán)限系統(tǒng):
注:一般在用戶交互后使用,不在頁面加載時(shí)立即請(qǐng)求

(3)實(shí)際應(yīng)用
class BatchFileUploader {
constructor() {
this.dirHandle = null;
this.files = [];
}
// 選擇目錄
async selectDirectory() {
try {
this.dirHandle = await showDirectoryPicker({
id: 'upload-directory',
mode: 'read'
});
// 讀取目錄中的所有圖片文件
await this.scanForImages();
return true;
} catch (err) {
console.error('選擇目錄失敗:', err);
return false;
}
}
// 掃描目錄中的圖片
async scanForImages() {
this.files = [];
for await (const [name, handle] of this.dirHandle.entries()) {
if (handle.kind === 'file') {
const file = await handle.getFile();
// 只處理圖片文件
if (file.type.startsWith('image/')) {
this.files.push({
name,
handle,
file,
size: file.size,
type: file.type,
lastModified: file.lastModified
});
}
}
}
console.log(`找到 ${this.files.length} 個(gè)圖片文件`);
}
// 批量上傳
async uploadAll() {
const results = [];
for (const fileInfo of this.files) {
try {
const result = await this.uploadFile(fileInfo);
results.push({ success: true, ...result });
} catch (error) {
results.push({ success: false, name: fileInfo.name, error });
}
}
return results;
}
async uploadFile(fileInfo) {
const formData = new FormData();
formData.append('file', fileInfo.file);
formData.append('path', fileInfo.name);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
return await response.json();
}
}7、DataTransfer API
是瀏覽器提供的用于在拖放操作中傳輸數(shù)據(jù)(例如一個(gè)快遞要從a處到b處,datatransfer對(duì)象就是快遞包裹;拖放事件就相當(dāng)于寄件,運(yùn)輸,收件的過程;拖放數(shù)據(jù)就相當(dāng)于包裹中的物品)。
(1)對(duì)象詳解
dropeffect:拖放操作(復(fù)制,移動(dòng),鏈接);
effectallowed:允許拖放的操作有哪些(權(quán)限設(shè)置)。


拖放的數(shù)據(jù)類型:

(2)基本使用
最簡(jiǎn)單的文本拖放
<!-- HTML結(jié)構(gòu) -->
<div id="source" draggable="true">拖拽我</div>
<div id="target">放到這里</div>
<script>
// JavaScript實(shí)現(xiàn)
const source = document.getElementById('source');
const target = document.getElementById('target');
// 1. 拖拽開始:準(zhǔn)備數(shù)據(jù)
source.addEventListener('dragstart', (event) => {
console.log('開始拖拽');
// 設(shè)置要傳輸?shù)臄?shù)據(jù)
event.dataTransfer.setData('text/plain', '這是傳輸?shù)臄?shù)據(jù)');
// 設(shè)置拖拽效果
event.dataTransfer.effectAllowed = 'copy';
});
// 2. 拖拽到目標(biāo)上方:允許放置
target.addEventListener('dragover', (event) => {
event.preventDefault(); // 必須調(diào)用,允許放置
event.dataTransfer.dropEffect = 'copy';
target.style.backgroundColor = '#e3f2fd';
});
// 3. 離開目標(biāo)區(qū)域
target.addEventListener('dragleave', () => {
target.style.backgroundColor = '';
});
// 4. 放置數(shù)據(jù)
target.addEventListener('drop', (event) => {
event.preventDefault();
// 獲取傳輸?shù)臄?shù)據(jù)
const data = event.dataTransfer.getData('text/plain');
target.textContent = `接收到: ${data}`;
target.style.backgroundColor = '';
console.log('數(shù)據(jù)放置完成:', data);
});
</script>(3)文件拖放
<div id="dropZone" style="border: 2px dashed #ccc; padding: 50px;">
將文件拖放到這里
<div id="fileList"></div>
</div>
<script>
const dropZone = document.getElementById('dropZone');
const fileList = document.getElementById('fileList');
// 阻止瀏覽器默認(rèn)行為(打開文件)
dropZone.addEventListener('dragover', (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
dropZone.style.borderColor = '#2196f3';
});
dropZone.addEventListener('dragleave', () => {
dropZone.style.borderColor = '#ccc';
});
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
dropZone.style.borderColor = '#4caf50';
// 獲取拖放的文件
const files = event.dataTransfer.files;
console.log('拖放的文件數(shù)量:', files.length);
// 處理每個(gè)文件
Array.from(files).forEach((file, index) => {
const fileInfo = document.createElement('div');
fileInfo.innerHTML = `
<strong>${index + 1}. ${file.name}</strong><br>
類型: ${file.type || '未知'}<br>
大小: ${(file.size / 1024).toFixed(2)} KB<br>
修改時(shí)間: ${new Date(file.lastModified).toLocaleString()}
<hr>
`;
fileList.appendChild(fileInfo);
// 如果是圖片,創(chuàng)建預(yù)覽
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement('img');
img.src = e.target.result;
img.style.maxWidth = '200px';
img.style.maxHeight = '150px';
img.style.margin = '10px';
fileInfo.appendChild(img);
};
reader.readAsDataURL(file);
}
});
});
</script>8、文件處理工具API:URLcreateObjectURL()
是一個(gè)瀏覽器api,它能將內(nèi)存中的Blob,F(xiàn)ile,MediaSource對(duì)象轉(zhuǎn)換成一個(gè)臨時(shí)URL地址。
特點(diǎn):
- 臨時(shí)性:只在當(dāng)前會(huì)話有效;
- 每個(gè)對(duì)象生成唯一的URL;
- 需要手動(dòng)釋放。
(1)基本語法

//簡(jiǎn)單示例
// 示例1:創(chuàng)建文本文件的下載鏈接
function createTextFileDownload() {
// 1. 創(chuàng)建Blob對(duì)象
const text = 'Hello, World! 這是要下載的內(nèi)容。';
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
// 2. 創(chuàng)建Object URL
const url = URL.createObjectURL(blob);
console.log('生成的URL:', url);
// 輸出: blob:http://localhost:3000/123e4567-e89b-12d3-a456-426614174000
// 3. 使用URL創(chuàng)建下載鏈接
const a = document.createElement('a');
a.href = url;
a.download = 'example.txt'; // 下載文件名
a.textContent = '點(diǎn)擊下載文本文件';
// 4. 添加到頁面并自動(dòng)點(diǎn)擊
document.body.appendChild(a);
a.click();
// 5. 清理(重要?。?
setTimeout(() => {
URL.revokeObjectURL(url);
document.body.removeChild(a);
console.log('URL已回收,內(nèi)存已釋放');
}, 100);
}
// 示例2:預(yù)覽圖片
function previewImage(file) {
// file是用戶通過<input type="file">選擇的File對(duì)象
// 1. 創(chuàng)建Object URL
const imageUrl = URL.createObjectURL(file);
// 2. 創(chuàng)建圖片元素并顯示
const img = document.createElement('img');
img.src = imageUrl;
img.style.maxWidth = '300px';
img.alt = file.name;
// 3. 添加到頁面
const container = document.getElementById('preview-container');
container.innerHTML = ''; // 清空之前的預(yù)覽
container.appendChild(img);
// 4. 圖片加載完成后清理(可選,看需求)
img.onload = () => {
console.log('圖片預(yù)覽已顯示,URL:', imageUrl);
// 注意:這里不立即revoke,因?yàn)閳D片還在顯示
};
// 返回URL以便后續(xù)清理
return imageUrl;
}(2)工作原理

(3)與Data URL對(duì)比
// 對(duì)比示例:同一個(gè)圖片的兩種不同表示
// Data URL方式(數(shù)據(jù)在URL中)
const dataUrl = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==`;
// Blob URL方式(數(shù)據(jù)在內(nèi)存中,URL是引用)
const blob = base64ToBlob(base64Data, 'image/png');
const blobUrl = URL.createObjectURL(blob);
// 比較表
const comparison = {
DataURL: {
存儲(chǔ)方式: '數(shù)據(jù)內(nèi)嵌在URL字符串中',
長(zhǎng)度: '很長(zhǎng)(數(shù)據(jù)越大URL越長(zhǎng))',
內(nèi)存: '每個(gè)使用處都有一份完整副本',
更新: '需要重新生成整個(gè)URL',
適用: '小于10KB的小資源'
},
BlobURL: {
存儲(chǔ)方式: '數(shù)據(jù)在內(nèi)存中,URL是引用',
長(zhǎng)度: '固定長(zhǎng)度,與數(shù)據(jù)大小無關(guān)',
內(nèi)存: '數(shù)據(jù)只有一份,多處引用',
更新: '更新Blob內(nèi)容,URL不變',
適用: '任意大小的資源'
}
};(4)實(shí)際應(yīng)用
文件預(yù)覽系統(tǒng)
class FilePreviewSystem {
constructor() {
this.activeUrls = new Set(); // 跟蹤活躍的URL
this.previewHistory = []; // 預(yù)覽歷史記錄
}
// 預(yù)覽單個(gè)文件
previewFile(file) {
if (!file) return null;
// 清理之前的預(yù)覽(如果需要)
this.cleanupPreviousPreviews();
// 創(chuàng)建Object URL
const url = URL.createObjectURL(file);
this.activeUrls.add(url);
// 根據(jù)文件類型選擇預(yù)覽方式
if (file.type.startsWith('image/')) {
this.previewImage(url, file.name);
} else if (file.type.startsWith('video/')) {
this.previewVideo(url, file.name);
} else if (file.type.startsWith('audio/')) {
this.previewAudio(url, file.name);
} else if (file.type === 'application/pdf') {
this.previewPDF(url, file.name);
} else {
this.previewAsText(url, file);
}
// 記錄歷史
this.previewHistory.push({
url,
fileName: file.name,
timestamp: Date.now(),
type: file.type
});
return url;
}
// 預(yù)覽圖片
previewImage(url, fileName) {
const container = document.getElementById('preview-container');
// 創(chuàng)建圖片元素
const img = document.createElement('img');
img.src = url;
img.alt = fileName;
img.className = 'preview-image';
// 添加加載和錯(cuò)誤處理
img.onload = () => {
console.log(`圖片加載成功: ${fileName}`);
this.adjustPreviewSize(img);
};
img.onerror = () => {
console.error(`圖片加載失敗: ${fileName}`);
this.showErrorMessage('圖片加載失敗');
};
container.innerHTML = '';
container.appendChild(img);
// 添加清理回調(diào)
img.addEventListener('unload', () => {
this.revokeUrl(url);
});
}
// 預(yù)覽視頻
previewVideo(url, fileName) {
const container = document.getElementById('preview-container');
const video = document.createElement('video');
video.src = url;
video.controls = true;
video.className = 'preview-video';
// 添加元數(shù)據(jù)
const meta = document.createElement('div');
meta.className = 'video-meta';
meta.textContent = `視頻: ${fileName}`;
container.innerHTML = '';
container.appendChild(video);
container.appendChild(meta);
}
// 批量預(yù)覽文件
previewMultipleFiles(files) {
const urls = [];
const gallery = document.createElement('div');
gallery.className = 'file-gallery';
Array.from(files).forEach((file, index) => {
if (file.type.startsWith('image/')) {
const url = URL.createObjectURL(file);
urls.push(url);
this.activeUrls.add(url);
const img = document.createElement('img');
img.src = url;
img.alt = file.name;
img.className = 'gallery-thumb';
img.dataset.index = index;
// 點(diǎn)擊查看大圖
img.addEventListener('click', () => {
this.showFullscreenPreview(url, file.name);
});
gallery.appendChild(img);
}
});
const container = document.getElementById('preview-container');
container.innerHTML = '';
container.appendChild(gallery);
return urls;
}
// 清理資源
cleanupPreviousPreviews() {
// 可以根據(jù)策略決定是否立即清理
// 這里清理除當(dāng)前活躍外的所有URL
this.activeUrls.forEach(url => {
URL.revokeObjectURL(url);
});
this.activeUrls.clear();
}
// 安全地撤銷URL
revokeUrl(url) {
if (this.activeUrls.has(url)) {
URL.revokeObjectURL(url);
this.activeUrls.delete(url);
console.log(`已釋放URL: ${url}`);
}
}
// 頁面卸載時(shí)清理所有資源
setupCleanupOnUnload() {
window.addEventListener('beforeunload', () => {
this.cleanupAllResources();
});
// 防止內(nèi)存泄漏
window.addEventListener('pagehide', () => {
this.cleanupAllResources();
});
}
cleanupAllResources() {
console.log('清理所有Blob URL資源');
this.activeUrls.forEach(url => {
try {
URL.revokeObjectURL(url);
} catch (e) {
console.warn('清理URL時(shí)出錯(cuò):', e);
}
});
this.activeUrls.clear();
this.previewHistory = [];
}
}9、Fetch API
是一個(gè)基于promise的JavaScript API,用于發(fā)起網(wǎng)絡(luò)請(qǐng)求,代替了傳統(tǒng)的XMLHttpRequest。
(1)基本用法
a、簡(jiǎn)單的GET請(qǐng)求
// 基礎(chǔ)GET請(qǐng)求
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('網(wǎng)絡(luò)響應(yīng)不正常');
}
return response.json(); // 解析JSON響應(yīng)
})
.then(data => {
console.log('獲取到的數(shù)據(jù):', data);
// 處理數(shù)據(jù)
})
.catch(error => {
console.error('請(qǐng)求失敗:', error);
// 錯(cuò)誤處理
});
// 實(shí)際示例:獲取GitHub用戶信息
async function getGitHubUser(username) {
try {
const response = await fetch(`https://api.github.com/users/${username}`);
if (response.status === 404) {
throw new Error('用戶不存在');
}
if (!response.ok) {
throw new Error(`HTTP錯(cuò)誤: ${response.status}`);
}
const userData = await response.json();
console.log(`${username}的信息:`, userData);
return userData;
} catch (error) {
console.error('獲取用戶信息失敗:', error);
return null;
}
}
// 使用
getGitHubUser('octocat').then(user => {
if (user) {
console.log(`用戶名: ${user.login}, 粉絲: ${user.followers}`);
}
});b、Fetch配置選項(xiàng)
// Fetch的完整配置對(duì)象
const fetchOptions = {
// 請(qǐng)求方法
method: 'GET', // GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
// 請(qǐng)求頭
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here',
'User-Agent': 'MyApp/1.0',
'Accept': 'application/json'
},
// 請(qǐng)求體(GET和HEAD請(qǐng)求不能有body)
body: JSON.stringify({ key: 'value' }),
// 請(qǐng)求模式
mode: 'cors', // cors, no-cors, same-origin
// 緩存策略
cache: 'default', // default, no-store, reload, no-cache, force-cache, only-if-cached
// 是否發(fā)送憑證(cookies)
credentials: 'same-origin', // omit, same-origin, include
// 重定向策略
redirect: 'follow', // follow, error, manual
// 引用策略
referrerPolicy: 'no-referrer-when-downgrade',
// 完整性校驗(yàn)
integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=',
// 請(qǐng)求超時(shí)(注意:Fetch原生不支持timeout,需要AbortController)
signal: abortController.signal,
// 優(yōu)先級(jí)
priority: 'auto' // high, low, auto
};c、HTTP方法示例
class ApiClient {
// GET請(qǐng)求:獲取數(shù)據(jù)
async getData(url, queryParams = {}) {
// 構(gòu)建查詢字符串
const queryString = new URLSearchParams(queryParams).toString();
const fullUrl = queryString ? `${url}?${queryString}` : url;
const response = await fetch(fullUrl, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
return this.handleResponse(response);
}
// POST請(qǐng)求:創(chuàng)建資源
async createData(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(data)
});
return this.handleResponse(response);
}
// PUT請(qǐng)求:更新整個(gè)資源
async updateData(url, data) {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(data)
});
return this.handleResponse(response);
}
// PATCH請(qǐng)求:部分更新
async patchData(url, partialData) {
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(partialData)
});
return this.handleResponse(response);
}
// DELETE請(qǐng)求:刪除資源
async deleteData(url) {
const response = await fetch(url, {
method: 'DELETE'
});
return this.handleResponse(response, false); // 可能沒有響應(yīng)體
}
// 處理響應(yīng)
async handleResponse(response, expectJson = true) {
if (!response.ok) {
const errorText = await response.text();
throw new Error(`請(qǐng)求失敗: ${response.status} - ${errorText}`);
}
if (response.status === 204) { // No Content
return null;
}
if (expectJson) {
return await response.json();
}
return response;
}
}
// 使用示例
const api = new ApiClient();
// 獲取用戶列表
api.getData('/api/users', { page: 1, limit: 10 })
.then(users => console.log('用戶列表:', users));
// 創(chuàng)建新用戶
api.createData('/api/users', {
name: '張三',
email: 'zhangsan@example.com',
age: 25
}).then(newUser => console.log('創(chuàng)建的用戶:', newUser));(2)Response對(duì)象詳情
response對(duì)象的屬性和方法
// 響應(yīng)對(duì)象的結(jié)構(gòu)
const response = {
// 屬性
ok: true, // HTTP狀態(tài)碼在200-299范圍內(nèi)為true
status: 200, // HTTP狀態(tài)碼
statusText: 'OK', // 狀態(tài)文本
url: 'https://api.example.com/data', // 請(qǐng)求的URL
redirected: false, // 是否被重定向
type: 'cors', // basic, cors, error, opaque, opaqueredirect
// Headers對(duì)象
headers: Headers {}, // 響應(yīng)頭
// 讀取響應(yīng)體的方法
arrayBuffer(): Promise<ArrayBuffer>,
blob(): Promise<Blob>,
formData(): Promise<FormData>,
json(): Promise<any>,
text(): Promise<string>,
// 克隆響應(yīng)
clone(): Response,
// 其他
body: ReadableStream, // 響應(yīng)體流
bodyUsed: false // 是否已讀取過響應(yīng)體
};
// 實(shí)際使用示例
async function processResponse(url) {
const response = await fetch(url);
console.log('狀態(tài)碼:', response.status);
console.log('狀態(tài)文本:', response.statusText);
console.log('請(qǐng)求成功?', response.ok);
console.log('請(qǐng)求URL:', response.url);
console.log('是否重定向?', response.redirected);
console.log('響應(yīng)類型:', response.type);
// 檢查響應(yīng)頭
console.log('內(nèi)容類型:', response.headers.get('Content-Type'));
console.log('內(nèi)容長(zhǎng)度:', response.headers.get('Content-Length'));
// 遍歷所有響應(yīng)頭
response.headers.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 根據(jù)內(nèi)容類型選擇解析方式
const contentType = response.headers.get('Content-Type');
let data;
if (contentType.includes('application/json')) {
data = await response.json();
} else if (contentType.includes('text/')) {
data = await response.text();
} else if (contentType.includes('image/')) {
data = await response.blob();
} else if (contentType.includes('multipart/form-data')) {
data = await response.formData();
} else {
data = await response.arrayBuffer();
}
return data;
}(3)實(shí)際應(yīng)用
a、上傳文件
class FileUploader {
constructor() {
this.chunkSize = 1024 * 1024; // 1MB分片
}
// 1. 簡(jiǎn)單文件上傳
async uploadFile(file, uploadUrl) {
const formData = new FormData();
formData.append('file', file);
formData.append('fileName', file.name);
formData.append('fileSize', file.size);
formData.append('uploadTime', new Date().toISOString());
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData
// 注意:使用FormData時(shí)不要設(shè)置Content-Type頭,瀏覽器會(huì)自動(dòng)設(shè)置
});
return response.json();
}
// 2. 大文件分片上傳
async uploadLargeFile(file, uploadUrl, onProgress) {
const fileSize = file.size;
const totalChunks = Math.ceil(fileSize / this.chunkSize);
const fileId = `${Date.now()}_${Math.random().toString(36).substr(2)}`;
let uploadedChunks = 0;
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const start = chunkIndex * this.chunkSize;
const end = Math.min(start + this.chunkSize, fileSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('fileId', fileId);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('fileName', file.name);
formData.append('chunk', chunk);
try {
const response = await fetch(`${uploadUrl}/chunk`, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`分片上傳失敗: ${response.status}`);
}
uploadedChunks++;
// 更新進(jìn)度
if (onProgress) {
const progress = (uploadedChunks / totalChunks) * 100;
onProgress(progress, chunkIndex, totalChunks);
}
console.log(`分片 ${chunkIndex + 1}/${totalChunks} 上傳成功`);
} catch (error) {
console.error(`分片 ${chunkIndex} 上傳失敗:`, error);
// 可以選擇重試或放棄
throw error;
}
}
// 通知服務(wù)器合并分片
const mergeResponse = await fetch(`${uploadUrl}/merge`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
fileId,
fileName: file.name,
totalChunks
})
});
return mergeResponse.json();
}
// 3. 支持拖放上傳
setupDropZone(dropZoneElement, onFileSelect) {
dropZoneElement.addEventListener('dragover', (event) => {
event.preventDefault();
dropZoneElement.classList.add('drag-over');
});
dropZoneElement.addEventListener('dragleave', () => {
dropZoneElement.classList.remove('drag-over');
});
dropZoneElement.addEventListener('drop', (event) => {
event.preventDefault();
dropZoneElement.classList.remove('drag-over');
const files = event.dataTransfer.files;
if (files.length > 0 && onFileSelect) {
onFileSelect(files[0]);
}
});
}
// 4. 帶進(jìn)度顯示的上傳
async uploadWithProgress(file, uploadUrl) {
const xhr = new XMLHttpRequest(); // 注意:這里用XHR為了進(jìn)度事件
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('file', file);
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
console.log(`上傳進(jìn)度: ${percentComplete.toFixed(2)}%`);
this.updateProgressBar(percentComplete);
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`上傳失敗: ${xhr.status}`));
}
});
xhr.addEventListener('error', () => {
reject(new Error('網(wǎng)絡(luò)錯(cuò)誤'));
});
xhr.addEventListener('abort', () => {
reject(new Error('上傳被取消'));
});
xhr.open('POST', uploadUrl);
xhr.send(formData);
});
}
}
// 使用示例
const uploader = new FileUploader();
// 簡(jiǎn)單上傳
document.getElementById('fileInput').addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
try {
const result = await uploader.uploadFile(file, '/api/upload');
console.log('上傳成功:', result);
} catch (error) {
console.error('上傳失敗:', error);
}
});
// 大文件分片上傳
async function uploadLargeFile() {
const file = document.getElementById('largeFile').files[0];
uploader.uploadLargeFile(file, '/api/upload', (progress) => {
document.getElementById('progress').value = progress;
document.getElementById('progressText').textContent = `${progress.toFixed(1)}%`;
}).then(result => {
console.log('大文件上傳完成:', result);
}).catch(error => {
console.error('大文件上傳失敗:', error);
});
}b、請(qǐng)求超時(shí)控制
// Fetch原生不支持timeout,需要使用AbortController
class FetchWithTimeout {
constructor(defaultTimeout = 10000) {
this.defaultTimeout = defaultTimeout;
}
// 帶超時(shí)的fetch
async fetchWithTimeout(url, options = {}) {
const { timeout = this.defaultTimeout, ...fetchOptions } = options;
// 創(chuàng)建AbortController
const controller = new AbortController();
const { signal } = controller;
// 設(shè)置超時(shí)定時(shí)器
const timeoutId = setTimeout(() => {
controller.abort();
console.warn(`請(qǐng)求超時(shí): ${url}, 超時(shí)時(shí)間: ${timeout}ms`);
}, timeout);
try {
const response = await fetch(url, {
...fetchOptions,
signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP錯(cuò)誤: ${response.status}`);
}
return response;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error(`請(qǐng)求超時(shí) (${timeout}ms)`);
}
throw error;
}
}
// 使用示例
async getDataWithTimeout() {
try {
const response = await this.fetchWithTimeout('https://api.example.com/slow-data', {
timeout: 5000, // 5秒超時(shí)
headers: {
'Accept': 'application/json'
}
});
return await response.json();
} catch (error) {
console.error('請(qǐng)求失敗:', error.message);
return null;
}
}
}
// 批量請(qǐng)求,單個(gè)失敗不影響其他
class BatchFetcher {
constructor(maxConcurrent = 3) {
this.maxConcurrent = maxConcurrent;
this.pendingRequests = new Set();
}
async fetchAll(urls, options = {}) {
const results = [];
const urlQueue = [...urls];
while (urlQueue.length > 0 || this.pendingRequests.size > 0) {
// 填充并發(fā)請(qǐng)求
while (this.pendingRequests.size < this.maxConcurrent && urlQueue.length > 0) {
const url = urlQueue.shift();
const request = this.fetchSingle(url, options);
this.pendingRequests.add(request);
request.finally(() => {
this.pendingRequests.delete(request);
});
}
// 等待至少一個(gè)請(qǐng)求完成
if (this.pendingRequests.size > 0) {
const completed = await Promise.race(this.pendingRequests);
results.push(completed);
}
}
return results;
}
async fetchSingle(url, options) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
return { url, success: false, error: `HTTP ${response.status}` };
}
const data = await response.json();
return { url, success: true, data };
} catch (error) {
return {
url,
success: false,
error: error.name === 'AbortError' ? '請(qǐng)求超時(shí)' : error.message
};
}
}
}
// 使用
const batchFetcher = new BatchFetcher(5);
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
// ... 更多URL
];
batchFetcher.fetchAll(urls).then(results => {
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log(`成功: ${successful.length}, 失敗: ${failed.length}`);
});總結(jié)
到此這篇關(guān)于JavaScript文件的操作方法總結(jié)大全的文章就介紹到這了,更多相關(guān)JS文件操作方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ClearTimeout消除閃動(dòng)實(shí)例代碼
本文給大家介紹ClearTimeout消除閃動(dòng)相關(guān)知識(shí),本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-02-02
基于Bootstrap 3 JQuery及RegExp的表單驗(yàn)證功能
這篇文章主要介紹了基于Bootstrap 3 JQuery及RegExp的表單驗(yàn)證功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
Area 區(qū)域?qū)崿F(xiàn)post提交數(shù)據(jù)的js寫法
這篇文章主要介紹了在Area區(qū)域的里 實(shí)現(xiàn)post 提交數(shù)據(jù) 的js寫法,需要的朋友可以參考下2014-04-04
4 種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較
這篇文章主要介紹了4種滾動(dòng)吸頂實(shí)現(xiàn)方式的比較,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
JavaScript+Canvas實(shí)現(xiàn)帶跳動(dòng)效果的粒子動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了如何通過JavaScript和Canvas實(shí)現(xiàn)帶跳動(dòng)效果的粒子動(dòng)畫,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2023-03-03



