JavaScript 读取及写入本地文件

概述

在纯前端 javaScript 中,浏览器环境压根没有直接提供操作文件系统的能力。它不能像 node.js 那样使用 fs 模块来删除或者创建文件。 针对于浏览器出于安全性的考虑,不允许网页随意访问用户的文件系统,以防止潜在的恶意行为。

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

文件上传和下载:
文件上传: 可以通过 <input type=“file”> 元素让用户选择文件,然后通过 JavaScript 读取文件内容。
文件下载: 可以通过创建 Blob 对象和使用 a 标签的 download 属性来触发文件下载。

File API

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

一:读文件

(一)最简单方式

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>读文件</title>
</head>
<body>
<input type="file" id="fileInput">
<button id="processButton">Process and Download</button><script>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!');}});
</script>
</body>
</html>

HTML 部分:

  • 创建了一个文件输入框 (<input type=“file”>) 让用户选择文件。

JavaScript 部分:

  • 创建了一个 FileReader 对象来读取选中的文件。
  • 使用reader.onload 指定成功读取文件时要做什么。
  • 使用 reader.readAsText(file)开始以文本形式读取文件。

(二)读取大文件

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

1,分片读取

使用 FileReaderreadAsArrayBuffer() 方法,然后使用 Blobslice() 方法来分块读取文件。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>读文件</title>
</head>
<body>
<input type="file" id="fileInput">
<button id="processButton">Process</button><script>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!');}});
</script>
</body>
</html>

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

2,使用 stream

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

<!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" id="fileInput">
<button id="processButton">Process File Stream</button><script>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; // Uint8Arrayconst 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!');}});
</script>
</body>
</html>
  • file.stream(): 这是 File 对象的新方法,返回一个 ReadableStream,用于读取文件内容。

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

  • reader.read(): 每次调用 reader .read() 方法,读取流中的一个块数据,返回一个 Promise,该 Promise 解析为一个对象,包含 donevalue 属性。

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

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

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

(三)注意

1,安全性问题

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

处理方法:

  • 用户明确选择: 必须通过文件选择对话框(如 <input type="file">)让用户主动选择文件,而不是让脚本直接访问。
    <input type="file" id="fileInput">
    
  • 文件处理的权限: 使用 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 APIslice() 方法或 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 < file.size) {readChunk(file); // 继续读取下一块}};reader.readAsText(chunk);
    }
    
  • 异步操作: 使用 async/awaitPromises 处理文件读取,以避免阻塞主线程,确保页面保持响应性。
4,兼容性问题

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

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

处理方法:

  • Feature Detection: 在使用某些文件 API 之前,检查浏览器是否支持该功能。使用 if 语句检查是否存在特定 API。
    if (window.showOpenFilePicker) {// 使用 File System Access API
    } else {// 回退到 <input type="file">
    }
    
5,用户体验问题

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

处理方法:

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

    <progress id="progressBar" value="0" max="100"></progress>	
    // 在读取文件块时更新进度条
    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,文件类型和内容验证

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

处理方法:

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

    <input type="file" 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;
    }
    

这里举个例子🌰:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件处理</title>
</head>
<body>
<input type="file" id="fileInput" accept=".pdf">
<button id="processButton">Process</button>
<progress id="progressBar" value="0" max="100" style="display: none;"></progress>
<p id="percentage">0%</p>
<p id="statusMessage"></p>
<script>document.getElementById('processButton').addEventListener('click', function () {const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];const MAX_SIZE = 300 * 1024 * 1024; // 最大300MBconst 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();});
</script>
</body>
</html>

三,写文件

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

(一)最常用的方法

最常用的方法时使用 BlobURL.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左右)。
  • 数据只存储在浏览器中,不是真正的文件。
  • 用户清除浏览器数据时会丢失。

一个完整的示例🌰:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件保存示例</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>文件保存示例</h1>
<textarea id="content" placeholder="在此输入要保存的内容"></textarea>
<div><button onclick="saveUsingBlob()">使用Blob下载</button><button onclick="saveUsingFileSystem()">使用File System API保存</button><button onclick="saveToLocalStorage()">保存到LocalStorage</button><button onclick="saveToIndexedDB()">保存到IndexedDB</button>
</div>
<div id="status"></div>
<script>// 使用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 APIasync 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');}}// 使用LocalStoragefunction saveToLocalStorage() {const content = document.getElementById('content').value;try {localStorage.setItem('savedContent', content);updateStatus('内容已保存到LocalStorage');} catch (err) {updateStatus('保存到LocalStorage时出错: ' + err);}}// 使用IndexedDBlet 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;}
</script>
</body>
</html>

本人也是记录一下,方便以后用到。附上原文地址,尊重原创!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/465692.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI - 使用LangChain请求LLM结构化生成内容

AI - 使用LangChain请求LLM结构化生成内容 基于深度学习的大型语言模型&#xff08;LLM&#xff09;不仅可以生成文本&#xff0c;还可以帮助我们完成许多复杂任务&#xff0c;如自动化客服、内容创作和数据分析。然而&#xff0c;如何从这些模型中结构化地获取输出&#xff0c…

使用GPT-SoVITS训练语音模型

1.项目演示 阅读单句话 1725352713141 读古诗 1725353700203 2.项目环境 开发环境&#xff1a;linux 机器配置如下&#xff1a;实际使用率百分之二十几&#xff0c; 3.开发步骤 1.首先是准备数据集&#xff0c;要求是wav格式&#xff0c;一到两个小时即可&#xff0c; 2.…

UML的另一个主角——用例图

顺序图和类图已经出过单集&#xff0c;本贴要分享的是用例图。 类图https://blog.csdn.net/jsl123x/article/details/143526286?spm1001.2014.3001.5501顺序图https://jslhyh32.blog.csdn.net/article/details/134350587 目录 一.系统 二.参与者 1.主要参与者 2.次要参与…

set和map的使用

目录 1.关联式容器 2.键值对 3.set 3.1set的模版参数列表 3.2对set的修改 3.2.1insert 3.2.2 erase 3.2.3clear 3.2.4swap 3.2.5 find 3.3set的迭代器 3.4set的容量 4.map 4.1对map的修改 4.1.1insert 4.1.2erase 4.1.3swap 4.1.4clear 4.2map的迭代器 4.3opera…

【5.10】指针算法-快慢指针将有序链表转二叉搜索树

一、题目 给定一个单链表&#xff0c;其中的 元素按升序排序 &#xff0c;将其转换为 高度平衡的二叉搜索树 。 本题中&#xff0c;一个高度平衡二叉树是指一个二叉树每个节点的左右两个子树的高度差的绝对值不超过 1。 示例: 给定的有序链表&#xff1a; [ -10 , -3 , 0 , …

dns服务器配置

主服务器 1.挂载点 mount /dev/sr0 /mnt 2.防火墙关闭 systemctl stop firewalld setenforce 0 3.下载bind软件 dnf install bind -y 4.进行正向解析配置 vim /etc/named.conf options { listen-on port 53 { 192.168.92.128; }; directo…

stable diffusion图生图

本节内容&#xff0c;给大家带来的是stable diffusion的图生图课程&#xff0c;我们在midjourney的课程中有学习过midjourney的图生图功能&#xff0c;即使用垫图的方式来引导AI绘制图片。图生图是AI绘图程序一个非常重要的功能&#xff0c;stable diffusion同样提供了类似的功…

论文阅读笔记:DRCT: Saving Image Super-Resolution away from Information Bottleneck

论文阅读笔记&#xff1a;DRCT: Saving Image Super-Resolution away from Information Bottleneck 1 背景1.1 问题1.2 本文提出的方法 2 创新点3 方法4 模块4.1 问题描述4.2 深度特征提取模块4.3 同任务渐进式训练策略 5 效果5.1 和SOTA方法对比 论文&#xff1a;https://arxi…

一周内从0到1开发一款 AR眼镜 相机应用?

目录 1. &#x1f4c2; 前言 2. &#x1f4a0; 任务拆分 2.1 产品需求拆分 2.2 开发工作拆分 3. &#x1f531; 开发实现 3.1 代码目录截图 3.2 app 模块 3.3 middleware 模块 3.4 portal 模块 4. ⚛️ 拍照与录像 4.1 前滑后滑统一处理 4.2 初始化 View 以及 Came…

【论文精读】LPT: Long-tailed prompt tuning for image classification

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 摘要 2. …

《重学Java设计模式》之 建造者模式

建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程 模拟装修公司对于设计出一些套餐装修服务的场景。 很多装修公司都会给出自家的套餐服务&#xff0c;一般有&#xff1b;豪华、轻奢、简约等&#xff0c;这些套餐的后面是不同的商品的…

Android Framework AMS(12)广播组件分析-3(广播发送流程解读)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读广播组件的广播发送过程。关注思维导图中左上侧部分即可。 有了前面广播组件 注册和注销程分析的基础&#xff0c;基于此&#xff…

MongoDB笔记02-MongoDB基本常用命令

文章目录 一、前言二、数据库操作2.1 选择和创建数据库2.2 数据库的删除 3 集合操作3.1 集合的显式创建3.2 集合的隐式创建3.3 集合的删除 四、文档基本CRUD4.1 文档的插入4.1.1 单个文档插入4.1.2 批量插入 4.2 文档的基本查询4.2.1 查询所有4.2.2 投影查询&#xff08;Projec…

MySQL基础-单表查询

语法 select [distinct] 列名1&#xff0c;列名2 as 别名... from数据表名 where组前筛选 group by分组字段 having组后筛选 order by排序的列 [asc | desc] limit 起始索引&#xff0c;数据条数 测试数据 # 建测试表 create table products (id int primary key a…

【pycharm jupyter】远程开发 启动报错

报错信息 upyter server process exited with code 1 ServerApp] A _jupyter_server_extension_points function was not found in jupyter_lsp. Instead, a _jupyter_server_extension_paths function was found and will be used for now. This function name will be depre…

CPU Study - Instructions Fetch

参考来源&#xff1a;《超标量处理器设计》—— 姚永斌 N-Way CPU 取指问题 如果CPU可以在每个周期内同时解码N条指令&#xff0c;则此类CPU为N-Way超标量处理器。 N-Way超标量处理器需要每个周期从I-Cache中至少取得N条指令&#xff0c;这N条指令成为一组Fetch Group。 为了…

掌握 PyQt5:从零开始的桌面应用开发

PyQT5——图形化界面 文章目录 PyQT5——图形化界面集成化图形界面工具为什么使用 \$ProjectFileDir$?示例场景其他 Varaiablespyuic参数解释整体含义示例使用PyQt5和pyuic 创建pyqt5的程序创建一个窗口app.exec\_()和sys.exit(app.exec_())的区别1. app.exec_()2. sys.exit(a…

论文阅读笔记:Image Processing GNN: Breaking Rigidity in Super-Resolution

论文阅读笔记&#xff1a;Image Processing GNN: Breaking Rigidity in Super-Resolution 1 背景2 创新点3 方法4 模块4.1 以往SR模型的刚性4.2 图构建4.2.1 度灵活性4.2.2 像素节点灵活性4.2.3 空间灵活性 4.3 图聚合4.4 多尺度图聚合模块MGB4.5 图聚合层GAL 5 效果5.1 和SOTA…

PMP–一、二、三模、冲刺–分类–7.成本管理–技巧–挣值分析

文章目录 技巧一模7.成本管理--4.控制成本--数据分析--挣值分析--进度绩效指数&#xff08;SPI&#xff09;是测量进度效率的一种指标&#xff0c;表示为挣值与计划价值之比&#xff0c;反映了项目团队完成工作的效率。 当 SPI小于 1.0 时&#xff0c;说明已完成的工作量未达到…

保姆级教程!!教你通过【Pycharm远程】连接服务器运行项目代码

小罗碎碎念 这篇文章主要解决一个问题——我有服务器&#xff0c;但是不知道怎么拿来写代码&#xff0c;跑深度学习项目。确实&#xff0c;玩深度学习的成本比较高&#xff0c;无论是前期的学习成本&#xff0c;还是你需要具备的硬件成本&#xff0c;都是拦路虎。小罗没有办法…