Overview
In pure front-end javaScript, the browser environment does not directly provide the ability to operate the file system. It cannot use the fs module to delete or create files like it does. For the sake of security, web pages are not allowed to access the user's file system at will to prevent potential malicious behavior.
The browser does provide some limited file operation capabilities, mainly through the following methods:
File upload and download:
File upload: can be passed<input type=“file”>
Elements let the user select the file and then read the file contents through JavaScript.
File download: Can be createdBlobObjects and usagea
Tagsdownload
Properties to trigger file download.
File API
File System Access API
File System Access API
It is a new API introduced by modern browsers (mostly in the Chromium kernel) that allows web pages to interact directly with the user's file system to create, read, write and delete files. This is the closest capability available to the file system operation provided by the current browser.
1: Read the file
(1) The easiest way
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Read the file</title> </head> <body> <input type="file" > <button >Process and Download</button> <script> ('processButton').addEventListener('click', function () { const fileInput = ('fileInput'); const file = [0]; if (file) { const reader = new FileReader(); = function (e) { // Read file content let content = ; (content); }; // Start reading the file (file); } else { alert('Please select a file first!'); } }); </script> </body> </html>
HTML section:
- Created a file input box (
<input type=“file”>
) Let the user select the file.
JavaScript section:
- A FileReader object is created to read the selected file.
- use
Specifies what to do when the file is successfully read.
- use
(file)
Start reading the file in text.
(II) Read large files
In the above code, the file is read throughFileReaderofreadAsText()
The method is completed. This method does load the entire file content into memory at once. This is no problem for small files, but if the file is very large, it can cause excessive memory usage, affect performance, and even cause page crashes.
1. Sharding reading
useFileReaderofreadAsArrayBuffer()
Method, then useBlobofslice()
Method to read files in chunks.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Read the file</title> </head> <body> <input type="file" > <button >Process</button> <script> ('processButton').addEventListener('click', function () { const fileInput = ('fileInput'); const file = [0]; if (file) { const CHUNK_SIZE = 1024 * 1024; // 1MB chunk size let offset = 0; // Functions that recursively read files function readNextChunk() { // Check whether it has been read to the end of the file if (offset >= ) { ("File processing complete."); return; } // Read the current block const chunk = (offset, offset + CHUNK_SIZE); const reader = new FileReader(); = function (e) { // Process the data of the current block let content = ; (`Processing chunk from ${offset} to ${offset + CHUNK_SIZE}`); (content); // More complex processing can be performed here // Update the offset and read the next block offset += CHUNK_SIZE; readNextChunk(); }; = function (e) { ("Error reading file chunk:", e); }; // Start reading the current block (chunk); } // Start reading the first block readNextChunk(); } else { alert('Please select a file first!'); } }); </script> </body> </html>
Since file reading is an asynchronous operation, recursively call readNextChunk() to continue with the next block of processing after each block of data is completed.
2. Use stream
Using the File APIstream()
Method (supported in newer browsers), which allows you to read files in streaming ways.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Stream Read File</title> </head> <body> <input type="file" > <button >Process File Stream</button> <script> ('processButton').addEventListener('click', async function () { const fileInput = ('fileInput'); const file = [0]; if (file) { const stream = (); const reader = (); // Read stream data async function read() { let result; while (!(result = await ()).done) { const chunk = ; // Uint8Array const textChunk = new TextDecoder().decode(chunk); // Convert to text (textChunk); // Process data blocks } ("File processing complete."); } // Start reading read().catch(error => ("Stream read error:", error)); } else { alert('Please select a file first!'); } }); </script> </body> </html>
-
()
: This is a new method for the File object, returning aReadableStream, used to read file contents. -
()
: By calling()
Get the stream's reader, returnReadableStreamDefaultReaderObject. -
()
: Each time the reader .read() method is called, a block of data is read in the stream and aPromise
, the Promise resolves to an object, containingdone
andvalue
property.
done: If true, it means that the stream has been read.
value: The currently read data block, returned in the form of Uint8Array.
-
TextDecoder
: Used toUint8Array
The data block is converted into readable text. For non-text data, additional processing can be performed as needed. - while loop: by
while
The loop keeps reading the file stream until the stream ends. - pass
async/await
andPromises
Implement concise asynchronous file reading logic.
(III) Attention
1. Security issues
Issue: For security reasons, the browser restricts direct access to the user's file system to prevent malicious scripts from accessing sensitive files or data without the user's consent. The front-end code can only access files through explicitly selected methods for users, such as<input type="file">
orFile System Access API。
How to deal with it:
User explicit selection: The file selection dialog must be passed (e.g.<input type="file">
) Let the user actively select files instead of allowing the script to access directly.
<input type="file" >
File processing permissions: UseFile System Access API(likeshowOpenFilePicker()
) When the browser explicitly requests permission from the user. Make sure to request only minimum permissions if necessary.
async function selectFile() { const [fileHandle] = await (); const file = await (); (); }
Keep permissions minimized: Only request the required files or directories, without attempting to access the entire file system. Limit the scope of operations, such as allowing only reads, avoiding writes or delete operations. 2. Privacy Issues
Problem: User files may contain sensitive information, such as personal data, financial information, etc. When the front-end reads files, it is necessary to ensure that the user's privacy is not leaked or abused.
How to deal with it:
- Transparency: Clearly inform the user of the content and purpose of the file to be read, avoiding reading data without the user's knowledge.
- Local processing: Try to process file content locally and avoid uploading data to the server or sending it to third-party services unless the user express consent is obtained.
const reader = new FileReader(); = function(e) { const content = ; // Process data only locally}; (file);
Data Cleanup: If you need to transfer file contents to the server, make sure to encrypt sensitive data and clean up unnecessary data after processing.
3. Performance issues
Question: When processing large files on the front end, it may cause excessive memory usage or lag in the browser, affecting the user experience.
How to deal with it:
Block processing: For large files, useFile APIofslice()
Method orstream()
Methods read files in chunks and process file contents step by step to avoid loading the entire file into memory at once.
const CHUNK_SIZE = 1024 * 1024; // 1MB let offset = 0; function readChunk(file) { const chunk = (offset, offset + CHUNK_SIZE); const reader = new FileReader(); = function(e) { const content = ; (content); // Process data blocks offset += CHUNK_SIZE; if (offset < ) { readChunk(file); // Continue to read the next block } }; (chunk); }
Asynchronous operation: Useasync/awaitorPromisesProcess file reads to avoid blocking the main thread and ensure that the page remains responsive.
4. Compatibility issues
Not all browsers support the latest File System Access API or some advanced file processing features. It is necessary to ensure that the code works properly in multiple browsers, or provide a reasonable fallback mechanism.
Problem: Not all browsers support the latest File System Access API or some advanced file processing features. It is necessary to ensure that the code works properly in multiple browsers, or provide a reasonable fallback mechanism.
How to deal with it:
Feature Detection: Before using certain file APIs, check whether the browser supports this feature. useif
Statement checks for a specific API.
if () { // Use the File System Access API} else { // Fall back to <input type="file">}
5. User experience issues
Problem: Front-end file operations usually involve users selecting files, uploading files, downloading files, etc. A good user experience can improve user satisfaction.
How to deal with it:
Progress Instructions: When processing large files, display a progress indicator (such as a progress bar) to let users understand the progress of file processing and prevent users from feeling that the application is stuck.
<progress value="0" max="100"></progress> // Update progress bar when reading file blocks = (offset / ) * 100;
Error handling: Provides friendly error prompts and handling mechanisms to help users understand problems and take action (such as reselecting files).
= function(e) { alert('Error reading file: ' + ); };
Feedback and confirmation: When the file operation is successfully completed, give feedback to the user, such as prompting the file to be processed or confirming that the download has been completed.
6. Permission management issues
Issue: File operations may involve permission issues, such as permissions may be revoked when accessing a file system through the File System Access API.
How to deal with it:
Permission Check: Before each operation, check whether you still have permission to access files or directories. If the permission is revoked, the user is prompted to reauthorize.
const permission = await (); if (permission !== 'granted') { // Prompt the user to re-authorize}
Permission request: If there is no permission, you can use the requestPermission() method to actively request permissions.
const permission = await (); if (permission === 'granted') { // Perform file operations}
7. File type and content verification
Problem: Users may select a file of the wrong type, or upload a file containing malicious content.
How to deal with it:
File Type Filtering: Use<input type="file">
Elementalaccept
Attributes limit the file type selected by the user. For example, restrict only .txt files are selected.
<input type="file" accept=".txt">
Content Verification: Verify the actual content format of the file before processing the file content. For example, if the file is in JSON format, you can try to parse the content and catch the error.
try { const data = (fileContent); } catch (e) { alert('Invalid JSON format'); }
8. File size limit
Problem: Handling very large files can cause memory overflow or performance issues.
How to deal with it:
Limit file size: Set file size limits in front-end code and check when the user selects a file. If the file is too large, give a prompt.
const MAX_SIZE = 10 * 1024 * 1024; // 10MB if ( > MAX_SIZE) { alert('File is too large!'); return; }
Here is an example 🌰:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File processing</title> </head> <body> <input type="file" accept=".pdf"> <button >Process</button> <progress value="0" max="100" style="display: none;"></progress> <p >0%</p> <p ></p> <script> ('processButton').addEventListener('click', function () { const fileInput = ('fileInput'); const file = [0]; const MAX_SIZE = 300 * 1024 * 1024; // Maximum 300MB const progressBar = ('progressBar'); const percentageDisplay = ('percentage'); const statusMessage = ('statusMessage'); // Reset status = 'none'; = 0; = '0%'; = ''; // Check whether the file is selected if (!file) { alert('Please select a file first!'); return; } // Check file size if ( > MAX_SIZE) { alert('File is too large! Please select a file under 300MB.'); return; } (`Selected file: ${}, ${} bytes, ${}`); // // Check file type (assuming only text files are accepted) // if ( !== "text/plain") { // alert('Invalid file type! Please select a .pdf file.'); // return; // } const CHUNK_SIZE = 1024 * 1024; // 1MB chunk size let offset = 0; // Show progress bar = 'block'; // Functions that recursively read files function readNextChunk() { // Check whether it has been read to the end of the file if (offset >= ) { = "File processing complete."; = 'none'; return; } // Read the current block const chunk = (offset, offset + CHUNK_SIZE); const reader = new FileReader(); = function (e) { // Process the data of the current block let content = ; (`Processing chunk from ${offset} to ${offset + CHUNK_SIZE}`); (content); // More complex processing can be performed here // Update the offset and read the next block offset += CHUNK_SIZE; // Calculate the percentage and update the display const percentage = ((offset / ) * 100, 100).toFixed(2); = percentage; = `${percentage}%`; readNextChunk(); }; = function (e) { ("Error reading file chunk:", e); = "Error reading file!"; = 'none'; }; // Start reading the current block (chunk); } // Start reading the first block readNextChunk(); }); </script> </body> </html>
2. Write a document
Writing information to local files in front-end code is a common requirement, but due to browser security limitations, this process is not as direct as it is on the back-end. We have several ways to implement this function, each with its advantages and disadvantages.
(I) The most commonly used method
The most commonly used method is usedBloband()
。
function saveToFile(content, filename) { const blob = new Blob([content], { type: 'text/plain' }); const url = (blob); const link = ('a'); = url; = filename; // This line is necessary to trigger downloads in the browser (link); (); // Clean and remove links (link); (url); } // Use examplesaveToFile('Hello, World!', '');
advantage:
- Widely supported, suitable for most modern browsers.
- Can handle large files.
- All kinds of data can be saved (not just text).
shortcoming:
- The user needs to select a save location and cannot directly write to a specific location.
- You cannot append content to existing files.
(II) Use the File System Access API
This is a newer API that provides more powerful file manipulation capabilities, but currently only supports it in some modern browsers.
async function writeToFile(content) { if ('showSaveFilePicker' in window) { try { const handle = await ({ types: [{ description: 'Text file', accept: { 'text/plain': ['.txt'] }, }], }); const writable = await (); await (content); await (); ('File saved successfully'); } catch (err) { ('Error saving file:', err); } } else { ('File System Access API not supported'); } } // Use examplewriteToFile('Hello, World!');
advantage:
- Provides more powerful file operation capabilities, including reading, writing and modifying files.
- You can access the file or directory selected by the user.
- Supports large files and streaming operations.
shortcoming:
- Browser support is limited, mainly new versions of Chrome and Edge.
- Users need to explicitly grant permissions.
(III) Use LocalStorage or IndexedDB
Instead of saving the data directly as a file, these methods store the data in the browser's local storage.
For LocalStorage:
function saveToLocalStorage(key, value) { (key, value); } // Use examplesaveToLocalStorage('myData', 'Hello, World!');
For IndexedDB, the code will be relatively complex, here is a simplified example:
let db; const dbName = "MyDatabase"; const request = (dbName, 1); = function(event) { ("Database error: " + ); }; = function(event) { db = ; ("Database opened successfully"); }; = function(event) { db = ; const objectStore = ("files", { keyPath: "id" }); }; function saveToIndexedDB(id, content) { const transaction = (["files"], "readwrite"); const objectStore = ("files"); const request = ({ id: id, content: content }); = function(event) { ("Error saving data: " + ); }; = function(event) { ("Data saved successfully"); }; } // Use example (need to be called after the database is opened)saveToIndexedDB('file1', 'Hello, World!');
advantage:
- Data can be saved without user interaction.
- Data persistence is stored in the browser.
- Suitable for storing application status or small datasets.
shortcoming:
- Storage capacity is limited (LocalStorage is usually limited to about 5MB).
- Data is stored only in the browser, not a real file.
- The user will lose when clearing the browser data.
A complete example 🌰:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File saving example</title> <style> 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; } </style> </head> <body> <h1>File saving example</h1> <textarea placeholder="Enter what you want to save here"></textarea> <div> <button onclick="saveUsingBlob()">useBlobdownload</button> <button onclick="saveUsingFileSystem()">useFile System APIsave</button> <button onclick="saveToLocalStorage()">save到LocalStorage</button> <button onclick="saveToIndexedDB()">save到IndexedDB</button> </div> <div ></div> <script> // Use Blob and () methods function saveUsingBlob() { const content = ('content').value; const blob = new Blob([content], {type: 'text/plain'}); const url = (blob); const link = ('a'); = url; = ''; (link); (); (link); (url); updateStatus('The file is ready to download'); } // Use File System Access API async function saveUsingFileSystem() { const content = ('content').value; if ('showSaveFilePicker' in window) { try { const handle = await ({ types: [{ description: 'Text file', accept: {'text/plain': ['.txt']}, }], }); const writable = await (); await (content); await (); updateStatus('File saved successfully'); } catch (err) { updateStatus('An error occurred while saving the file: ' + err); } } else { updateStatus('This browser does not support the File System Access API'); } } // Use LocalStorage function saveToLocalStorage() { const content = ('content').value; try { ('savedContent', content); updateStatus('Content saved to LocalStorage'); } catch (err) { updateStatus('An error occurred while saving to LocalStorage: ' + err); } } // Use IndexedDB let db; const dbName = "MyDatabase"; const dbVersion = 1; const request = (dbName, dbVersion); = function (event) { updateStatus("An error occurred while opening the database: " + ); }; = function (event) { db = ; updateStatus("The database has been opened successfully"); }; = function (event) { db = ; const objectStore = ("files", {keyPath: "id"}); updateStatus("Database created"); }; function saveToIndexedDB() { const content = ('content').value; if (!db) { updateStatus("The database is not ready"); return; } const transaction = (["files"], "readwrite"); const objectStore = ("files"); const request = ({id: "file1", content: content}); = function (event) { updateStatus("An error occurred while saving to IndexedDB: " + ); }; = function (event) { updateStatus("Content has been saved to IndexedDB successfully"); }; } function updateStatus(message) { ('status').textContent = message; } </script> </body> </html>
I will also record it, so it will be convenient for future use. Attach the original address and respect the original!
This is the article about JavaScript reading and writing local files. For more related JavaScript reading and writing local files, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!