如何使用jQuery无HTML接口读取本地文件上传并获取Blob正确MIME类型
本地路径无控件读取文件上传实现方案
首先明确运行环境边界:
浏览器端受同源沙箱安全策略限制,禁止JavaScript直接读取本地任意路径的文件,必须经过用户主动触发文件选择、文件拖拽等授权操作才能访问文件内容,不存在可稳定使用的绕过方案。你代码中使用的fs是Node.js runtime的原生API,仅能在Node.js、Electron等桌面端运行环境中使用。
下面分两种场景给出可直接运行的实现:
一、Node.js/Electron环境实现
你之前的代码存在三个问题:
- 创建Blob时没有显式传入MIME类型参数
- 打开的文件描述符没有手动关闭,会造成资源泄漏
- append到FormData时没有传入真实文件名,服务端会默认把文件命名为
blob
正确实现代码(Node 18+ 原生支持Blob、fetch,无需额外依赖)
const fs = require('fs/promises'); const path = require('path'); // 常用后缀与MIME类型映射,如果你需要覆盖更多类型,可以安装mime包,调用mime.getType(filePath)直接获取 const MIME_MAP = { '.txt': 'text/plain', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.pdf': 'application/pdf', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.zip': 'application/zip' }; async function uploadFile(localPath, uploadUrl) { // 读取文件二进制内容 const fileBuffer = await fs.readFile(localPath); // 获取文件名、后缀,匹配MIME类型 const fileName = path.basename(localPath); const ext = path.extname(localPath).toLowerCase(); const mimeType = MIME_MAP[ext] || 'application/octet-stream'; // 未知类型默认用二进制流 // 构造带正确MIME的Blob对象 const fileBlob = new Blob([fileBuffer], { type: mimeType }); // 构造上传表单 const formData = new FormData(); formData.append("test", "[69]"); formData.append("file", fileBlob, fileName); // 发起请求,如果你用jQuery,保持原来的processData:false、contentType:false配置即可 return fetch(uploadUrl, { method: 'POST', body: formData }); } // 调用示例 // uploadFile('C:/test.txt', 'https://你的上传接口地址')
如果你需要用同步写法,修正后的代码如下:
const fs = require('fs'); const path = require('path'); const localPath = 'C:/test.txt'; const fd = fs.openSync(localPath, 'r'); const stat = fs.statSync(localPath); const buf = Buffer.alloc(stat.size); fs.readSync(fd, buf, 0, stat.size, 0); fs.closeSync(fd); // 必须手动关闭文件描述符 const fileName = path.basename(localPath); const ext = path.extname(localPath).toLowerCase(); const mimeType = MIME_MAP[ext] || 'application/octet-stream'; const fileBlob = new Blob([buf], { type: mimeType }); formData.append('file', fileBlob, fileName);
二、纯浏览器环境实现
纯浏览器环境下无法直接读取指定本地路径的文件,如果不想显示原生文件输入控件,可以通过隐藏input、用自定义按钮触发选择事件的方式实现,本质还是走用户主动授权的流程,不存在安全问题,示例代码:
<!-- 隐藏原生文件选择控件 --> <input type="file" id="fileInput" style="display:none"> <button id="uploadBtn">点击上传文件</button> <script> const fileInput = document.getElementById('fileInput'); document.getElementById('uploadBtn').addEventListener('click', () => { fileInput.click(); }); fileInput.addEventListener('change', (e) => { const selectFile = e.target.files[0]; // 选中的File对象本身自带正确的MIME类型、文件名,直接构造表单上传即可 const formData = new FormData(); formData.append('file', selectFile); // 发起上传请求 $.ajax({ url: 'https://你的上传接口地址', method: 'POST', processData: false, contentType: false, data: formData }); }); </script>
补充说明
- 如果需要防止用户修改文件后缀伪造MIME类型,可以增加文件魔数校验逻辑:读取文件前2-4个字节的特征值判断真实文件类型,比如PNG文件开头固定为
89 50 4E 47、JPG开头固定为FF D8 FF,简单场景下用后缀匹配足够。 - 不要把所有文件的MIME都硬编码为
text/plain或者application/octet-stream,会导致服务端无法正确识别、处理文件。
内容的提问来源于stack exchange,提问作者Mauro Brx
相关产品推荐
相关产品推荐

