JavaScript:在前端代码中读、写本地文件

06-01 1237阅读

一,在前端代码JavaScript中读写文件的限制与处理

在前端 JavaScript 中,浏览器环境没有直接提供操作文件系统的能力。也就是说,你不能像在 Node.js 环境中那样,使用 fs 模块来删除或创建文件。这是因为浏览器出于安全性的考虑,不允许网页随意访问用户的文件系统,以防止潜在的恶意行为。

然而,浏览器确实提供了一些有限的文件操作能力,主要是通过以下几种方式:

1,文件上传和下载

- 文件上传: 可以通过 元素让用户选择文件,然后通过 JavaScript 读取文件内容。

- 文件下载: 可以通过创建 Blob 对象和使用 a 标签的 download 属性来触发文件下载。

2,File API

2,File System Access API

- File System Access API 是现代浏览器(主要是在 Chromium 内核的浏览器)引入的一种新 API,它允许网页直接与用户的文件系统交互,创建、读取、写入和删除文件。这是当前浏览器提供的最接近文件系统操作的能力。

二,读文件

(一)最简单方式



    
    
    读文件



Process and Download

    document.getElementById('processButton').addEventListener('click', function () {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function (e) {
                // 读取文件内容
                let content = e.target.result;
                console.log(content);
            };
            // 开始读取文件
            reader.readAsText(file);
        } else {
            alert('Please select a file first!');
        }
    });



HTML 部分:

- 我们创建了一个文件输入框 () 让用户选择文件。

JavaScript 部分:

- 我们创建了一个 FileReader 对象来读取选中的文件。

- 使用 reader.onload 指定成功读取文件时要做什么。

- 使用 reader.readAsText(file) 开始以文本形式读取文件。

(二)读取大文件

在上面的代码中,文件的读取是通过 FileReader 的 readAsText() 方法完成的。这个方法确实会一次性将整个文件内容加载到内存中。对于小型文件来说这没有问题,但如果文件非常大,可能会导致内存占用过高,影响性能,甚至导致页面崩溃。

JavaScript:在前端代码中读、写本地文件
(图片来源网络,侵删)

1,分片读取

使用 FileReader 的 readAsArrayBuffer() 方法,然后使用 Blob 的 slice() 方法来分块读取文件。



    
    
    读文件



Process

    document.getElementById('processButton').addEventListener('click', function () {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        if (file) {
            const CHUNK_SIZE = 1024 * 1024; // 1MB 分块大小
            let offset = 0;
            // 递归读取文件的函数
            function readNextChunk() {
                // 检查是否已经读取到文件末尾
                if (offset >= file.size) {
                    console.log("File processing complete.");
                    return;
                }
                // 读取当前块
                const chunk = file.slice(offset, offset + CHUNK_SIZE);
                const reader = new FileReader();
                reader.onload = function (e) {
                    // 处理当前块的数据
                    let content = e.target.result;
                    console.log(`Processing chunk from ${offset} to ${offset + CHUNK_SIZE}`);
                    console.log(content); // 此处可以进行更复杂的处理
                    // 更新偏移量,并读取下一块
                    offset += CHUNK_SIZE;
                    readNextChunk();
                };
                reader.onerror = function (e) {
                    console.error("Error reading file chunk:", e);
                };
                // 开始读取当前块
                reader.readAsText(chunk);
            }
            // 开始读取第一个块
            readNextChunk();
        } else {
            alert('Please select a file first!');
        }
    });



  • 由于文件读取是异步操作,递归调用 readNextChunk() 在每块数据处理完成后继续下一块处理。

    2,使用 stream

    使用 File API 的 stream() 方法(在较新的浏览器中支持),这允许你以流的方式读取文件。

    JavaScript:在前端代码中读、写本地文件
    (图片来源网络,侵删)
    
    
        
        
        Stream Read File
    
    
    
    Process File Stream
    
        document.getElementById('processButton').addEventListener('click', async function () {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (file) {
                const stream = file.stream();
                const reader = stream.getReader();
                // 读取流数据
                async function read() {
                    let result;
                    while (!(result = await reader.read()).done) {
                        const chunk = result.value; // Uint8Array
                        const textChunk = new TextDecoder().decode(chunk); // 转换为文本
                        console.log(textChunk); // 处理数据块
                    }
                    console.log("File processing complete.");
                }
                // 开始读取
                read().catch(error => console.error("Stream read error:", error));
            } else {
                alert('Please select a file first!');
            }
        });
    
    
    、
    
    • file.stream(): 这是 File 对象的新方法,返回一个 ReadableStream,用于读取文件内容。

    • stream.getReader(): 通过调用 stream.getReader() 获取流的读取器,返回 ReadableStreamDefaultReader 对象。

      JavaScript:在前端代码中读、写本地文件
      (图片来源网络,侵删)
    • reader.read(): 每次调用 reader.read() 方法,读取流中的一个块数据,返回一个 Promise,该 Promise 解析为一个对象,包含 done 和 value 属性。

      • done: 如果为 true,表示流已读取完毕。
      • value: 当前读取的数据块,以 Uint8Array 的形式返回。
      • TextDecoder: 用于将 Uint8Array 数据块转换为可读的文本。对于非文本数据,可以根据需要进行其他处理。

      • while 循环: 通过 while 循环不断读取文件流,直到流结束。

      • 通过 async/await 和 Promises 实现简洁的异步文件读取逻辑。

        (三)前端代码读取本地文件需要注意的地方

        1,安全性问题

        问题: 浏览器出于安全考虑,限制了对用户文件系统的直接访问,以防止恶意脚本未经用户同意访问敏感文件或数据。前端代码只能通过用户明确选择的方式访问文件,比如通过 或 File System Access API。

        处理方法:

        • 用户明确选择: 必须通过文件选择对话框(如 )让用户主动选择文件,而不是让脚本直接访问。
           
        • 文件处理的权限: 使用 File System Access API(如 showOpenFilePicker())时,浏览器会明确向用户请求权限。确保只在必要时请求最少的权限。
          async function selectFile() {
            const [fileHandle] = await window.showOpenFilePicker();
            const file = await fileHandle.getFile();
            console.log(file.name);
          }
          
          • 保持权限范围最小化: 只请求需要的文件或目录,不尝试访问整个文件系统。限制操作的范围,例如只允许读取,避免写入或删除操作。

            2,隐私问题

            问题: 用户文件可能包含敏感信息,如个人数据、财务信息等。前端读取文件时,必须确保用户的隐私不被泄露或滥用。

            处理方法:

            • 透明度: 明确告知用户文件将被读取的内容和目的,避免在用户不知情的情况下读取数据。
            • 本地处理: 尽量在本地处理文件内容,避免将数据上传到服务器或发送到第三方服务,除非获得用户明确同意。
              const reader = new FileReader();
              reader.onload = function(e) {
                const content = e.target.result;
                // 只在本地处理数据
              };
              reader.readAsText(file);
              
            • 数据清理: 如果需要将文件内容传输到服务器,确保对敏感数据进行加密,并在处理完毕后清理不再需要的数据。

              3,性能问题

              题: 在前端处理大文件时,可能会导致浏览器内存占用过高或卡顿,影响用户体验。

              处理方法:

              • 分块处理: 对于大文件,使用 File API 的 slice() 方法或 stream() 方法将文件分块读取,逐步处理文件内容,避免一次性将整个文件加载到内存中。
                const CHUNK_SIZE = 1024 * 1024; // 1MB
                let offset = 0;
                function readChunk(file) {
                  const chunk = file.slice(offset, offset + CHUNK_SIZE);
                  const reader = new FileReader();
                  reader.onload = function(e) {
                    const content = e.target.result;
                    console.log(content); // 处理数据块
                    offset += CHUNK_SIZE;
                    if (offset  
              • 异步操作: 使用 async/await 或 Promises 处理文件读取,以避免阻塞主线程,确保页面保持响应性。

                4,兼容性问题

                并非所有浏览器都支持最新的 File System Access API 或某些高级文件处理功能。需要确保代码在多个浏览器中都能正常工作,或者提供合理的回退机制。

                问题: 并非所有浏览器都支持最新的 File System Access API 或某些高级文件处理功能。需要确保代码在多个浏览器中都能正常工作,或者提供合理的回退机制。

                处理方法:

                • Feature Detection: 在使用某些文件 API 之前,检查浏览器是否支持该功能。使用 if 语句检查是否存在特定 API。
                  if (window.showOpenFilePicker) {
                    // 使用 File System Access API
                  } else {
                    // 回退到 
                  }
                  

                  5,用户体验问题

                  问题: 前端文件操作通常涉及用户选择文件、上传文件、下载文件等操作,良好的用户体验可以提升用户的满意度。

                  处理方法:

                  • 进度指示: 在处理大文件时,显示进度指示器(如进度条),让用户了解文件处理进度,避免用户感觉应用卡死。

                    	
                    // 在读取文件块时更新进度条
                    progressBar.value = (offset / file.size) * 100;
                    
                  • 错误处理: 提供友好的错误提示和处理机制,帮助用户理解问题并采取行动(如重新选择文件)。

                    reader.onerror = function(e) {
                      alert('Error reading file: ' + e.target.error.message);
                    };
                    
                  • 反馈和确认: 当文件操作成功完成时,给用户反馈,例如提示文件处理完毕,或确认下载已完成。

                    6,权限管理问题

                    问题: 文件操作可能涉及权限问题,例如通过 File System Access API 访问文件系统时,权限可能会被撤销。

                    处理方法:

                    • 权限检查: 每次操作前,检查是否仍有权限访问文件或目录。如果权限被撤销,提示用户重新授权。
                      const permission = await fileHandle.queryPermission();
                      if (permission !== 'granted') {
                        // 提示用户重新授权
                      }
                      
                    • 权限请求: 如果没有权限,可以使用 requestPermission() 方法主动请求权限。
                      const permission = await fileHandle.requestPermission();
                      if (permission === 'granted') {
                        // 执行文件操作
                      }
                      

                      7,文件类型和内容验证

                      问题: 用户可能会选择错误类型的文件,或上传包含恶意内容的文件。

                      处理方法:

                      • 文件类型过滤: 使用 元素的 accept 属性限制用户选择的文件类型。例如,限制只选择 .txt 文件。

                         
                      • 内容验证: 在处理文件内容之前,验证文件的实际内容格式。例如,如果文件是 JSON 格式,可以尝试解析内容并捕获错误。

                        try {
                          const data = JSON.parse(fileContent);
                        } catch (e) {
                          alert('Invalid JSON format');
                        }
                        

                        8,文件大小限制

                        问题: 处理非常大的文件可能会导致内存溢出或性能问题。

                        处理方法:

                        • 限制文件大小: 在前端代码中设置文件大小限制,并在用户选择文件时进行检查。如果文件过大,给出提示。
                          const MAX_SIZE = 10 * 1024 * 1024; // 10MB
                          if (file.size > MAX_SIZE) {
                            alert('File is too large!');
                            return;
                          }
                          

                          这里举个例子🌰:

                          
                          
                              
                              
                              文件处理
                          
                          
                          
                          Process
                          
                          

                          0%

                          document.getElementById('processButton').addEventListener('click', function () { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const MAX_SIZE = 300 * 1024 * 1024; // 最大300MB const progressBar = document.getElementById('progressBar'); const percentageDisplay = document.getElementById('percentage'); const statusMessage = document.getElementById('statusMessage'); // 重置状态 progressBar.style.display = 'none'; progressBar.value = 0; percentageDisplay.textContent = '0%'; statusMessage.textContent = ''; // 检查是否选择了文件 if (!file) { alert('Please select a file first!'); return; } // 检查文件大小 if (file.size > MAX_SIZE) { alert('File is too large! Please select a file under 300MB.'); return; } console.log(`Selected file: ${file.name}, ${file.size} bytes, ${file.type}`); // // 检查文件类型(假设只接受文本文件) // if (file.type !== "text/plain") { // alert('Invalid file type! Please select a .pdf file.'); // return; // } const CHUNK_SIZE = 1024 * 1024; // 1MB 分块大小 let offset = 0; // 显示进度条 progressBar.style.display = 'block'; // 递归读取文件的函数 function readNextChunk() { // 检查是否已经读取到文件末尾 if (offset >= file.size) { statusMessage.textContent = "File processing complete."; progressBar.style.display = 'none'; return; } // 读取当前块 const chunk = file.slice(offset, offset + CHUNK_SIZE); const reader = new FileReader(); reader.onload = function (e) { // 处理当前块的数据 let content = e.target.result; console.log(`Processing chunk from ${offset} to ${offset + CHUNK_SIZE}`); console.log(content); // 此处可以进行更复杂的处理 // 更新偏移量,并读取下一块 offset += CHUNK_SIZE; // 计算百分比并更新显示 const percentage = Math.min((offset / file.size) * 100, 100).toFixed(2); progressBar.value = percentage; percentageDisplay.textContent = `${percentage}%`; readNextChunk(); }; reader.onerror = function (e) { console.error("Error reading file chunk:", e); statusMessage.textContent = "Error reading file!"; progressBar.style.display = 'none'; }; // 开始读取当前块 reader.readAsText(chunk); } // 开始读取第一个块 readNextChunk(); });

                          三,写文件

                          在前端代码中将信息写入本地文件是一个常见的需求,但由于浏览器的安全限制,这个过程并不像在后端那样直接。我们有几种方法可以实现这个功能,每种方法都有其优缺点。

                          (一)最常用的方法

                          最常用的方法时使用 Blob 和 URL.createObjectURL()。

                          function saveToFile(content, filename) {
                              const blob = new Blob([content], { type: 'text/plain' });
                              const url = URL.createObjectURL(blob);
                              
                              const link = document.createElement('a');
                              link.href = url;
                              link.download = filename;
                              
                              // 这行是必要的,用于在浏览器中触发下载
                              document.body.appendChild(link);
                              
                              link.click();
                              
                              // 清理并移除链接
                              document.body.removeChild(link);
                              URL.revokeObjectURL(url);
                          }
                          // 使用示例
                          saveToFile('Hello, World!', 'example.txt');
                          

                          优点:

                          • 广泛支持,适用于大多数现代浏览器。
                          • 可以处理大文件。
                          • 可以保存各种类型的数据(不仅仅是文本)。

                            缺点:

                            • 用户需要选择保存位置,无法直接写入特定位置。
                            • 不能追加内容到现有文件。

                              (二)使用 File System Access API

                              这是一个较新的API,提供了更强大的文件操作能力,但目前只有部分现代浏览器支持。

                              async function writeToFile(content) {
                                  if ('showSaveFilePicker' in window) {
                                      try {
                                          const handle = await window.showSaveFilePicker({
                                              types: [{
                                                  description: 'Text file',
                                                  accept: { 'text/plain': ['.txt'] },
                                              }],
                                          });
                                          const writable = await handle.createWritable();
                                          await writable.write(content);
                                          await writable.close();
                                          console.log('File saved successfully');
                                      } catch (err) {
                                          console.error('Error saving file:', err);
                                      }
                                  } else {
                                      console.error('File System Access API not supported');
                                  }
                              }
                              // 使用示例
                              writeToFile('Hello, World!');
                              

                              优点:

                              • 提供更强大的文件操作能力,包括读取、写入和修改文件。
                              • 可以访问用户选择的文件或目录。
                              • 支持大文件和流式操作。

                                缺点:

                                • 浏览器支持有限,主要是新版Chrome和Edge。
                                • 需要用户明确授予权限。

                                  (三)使用 LocalStorage 或 IndexedDB

                                  这些方法不是直接将数据保存为文件,而是将数据存储在浏览器的本地存储中。

                                  对于 LocalStorage:

                                  function saveToLocalStorage(key, value) {
                                      localStorage.setItem(key, value);
                                  }
                                  // 使用示例
                                  saveToLocalStorage('myData', 'Hello, World!');
                                  

                                  对于 IndexedDB,代码会相对复杂一些,这里是一个简化的例子:

                                  let db;
                                  const dbName = "MyDatabase";
                                  const request = indexedDB.open(dbName, 1);
                                  request.onerror = function(event) {
                                      console.error("Database error: " + event.target.error);
                                  };
                                  request.onsuccess = function(event) {
                                      db = event.target.result;
                                      console.log("Database opened successfully");
                                  };
                                  request.onupgradeneeded = function(event) {
                                      db = event.target.result;
                                      const objectStore = db.createObjectStore("files", { keyPath: "id" });
                                  };
                                  function saveToIndexedDB(id, content) {
                                      const transaction = db.transaction(["files"], "readwrite");
                                      const objectStore = transaction.objectStore("files");
                                      const request = objectStore.put({ id: id, content: content });
                                      
                                      request.onerror = function(event) {
                                          console.error("Error saving data: " + event.target.error);
                                      };
                                      
                                      request.onsuccess = function(event) {
                                          console.log("Data saved successfully");
                                      };
                                  }
                                  // 使用示例(需要在数据库打开后调用)
                                  saveToIndexedDB('file1', 'Hello, World!');
                                  

                                  优点:

                                  • 不需要用户交互就能保存数据。
                                  • 数据持久化存储在浏览器中。
                                  • 适用于存储应用程序状态或小型数据集。

                                    缺点:

                                    • 存储容量有限(LocalStorage通常限制为5MB左右)。
                                    • 数据只存储在浏览器中,不是真正的文件。
                                    • 用户清除浏览器数据时会丢失。

                                      一个完整的示例🌰:

                                      
                                      
                                          
                                          
                                          文件保存示例
                                          
                                              body {
                                                  font-family: Arial, sans-serif;
                                                  max-width: 800px;
                                                  margin: 0 auto;
                                                  padding: 20px;
                                              }
                                              textarea {
                                                  width: 100%;
                                                  height: 100px;
                                                  margin-bottom: 10px;
                                              }
                                              button {
                                                  margin-right: 10px;
                                                  margin-bottom: 10px;
                                              }
                                          
                                      
                                      
                                      

                                      文件保存示例

                                      使用Blob下载 使用File System API保存 保存到LocalStorage 保存到IndexedDB
                                      // 使用Blob和URL.createObjectURL()方法 function saveUsingBlob() { const content = document.getElementById('content').value; const blob = new Blob([content], {type: 'text/plain'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'example.txt'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); updateStatus('文件已准备下载'); } // 使用File System Access API async function saveUsingFileSystem() { const content = document.getElementById('content').value; if ('showSaveFilePicker' in window) { try { const handle = await window.showSaveFilePicker({ types: [{ description: 'Text file', accept: {'text/plain': ['.txt']}, }], }); const writable = await handle.createWritable(); await writable.write(content); await writable.close(); updateStatus('文件保存成功'); } catch (err) { updateStatus('保存文件时出错: ' + err); } } else { updateStatus('此浏览器不支持File System Access API'); } } // 使用LocalStorage function saveToLocalStorage() { const content = document.getElementById('content').value; try { localStorage.setItem('savedContent', content); updateStatus('内容已保存到LocalStorage'); } catch (err) { updateStatus('保存到LocalStorage时出错: ' + err); } } // 使用IndexedDB let db; const dbName = "MyDatabase"; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = function (event) { updateStatus("打开数据库时出错: " + event.target.error); }; request.onsuccess = function (event) { db = event.target.result; updateStatus("数据库已成功打开"); }; request.onupgradeneeded = function (event) { db = event.target.result; const objectStore = db.createObjectStore("files", {keyPath: "id"}); updateStatus("数据库已创建"); }; function saveToIndexedDB() { const content = document.getElementById('content').value; if (!db) { updateStatus("数据库未准备好"); return; } const transaction = db.transaction(["files"], "readwrite"); const objectStore = transaction.objectStore("files"); const request = objectStore.put({id: "file1", content: content}); request.onerror = function (event) { updateStatus("保存到IndexedDB时出错: " + event.target.error); }; request.onsuccess = function (event) { updateStatus("内容已成功保存到IndexedDB"); }; } function updateStatus(message) { document.getElementById('status').textContent = message; }
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码