使用Crypto.js AES算法加密文件后上传服务器失败问题咨询
问题原因
- 异步执行顺序错误:
FileReader.readAsDataURL()是异步API,调用后不会立刻触发onload回调,你在调用读取方法后直接发起fetch请求,此时加密、append到formData的逻辑都还没执行,formData为空所以服务器收不到文件。 - 事件绑定逻辑错误:form的submit事件绑定被放在了文件选择框的change回调中,每次选择符合格式的文件都会新增一次submit监听,后续提交表单会触发多次重复请求。
- 加密逻辑异常:
CryptoJS.AES.encrypt()返回的是CipherParams对象而非字符串,直接传入File构造器会导致内容序列化错误;同时readAsDataURL读取的内容是带前缀的Base64 Data URI,不是文件原始二进制内容,加密后体积更大,也不利于后续解密还原。
解决方案
修正后完整代码
// 全局变量/常量声明 const url = 'process.php'; const form = document.querySelector('form'); const fileInput = document.querySelector("#inputGroupFile01"); const ALLOWED_EXT = /(.*?)\.(jpg|gif|doc|pdf|mp3|mp4|avi|txt|png|jpeg|tif)$/; const ENCRYPT_KEY = "ThisIsPasswordForTheFile"; // 表单提交事件绑定 移到外层避免重复绑定 form.addEventListener('submit', (e) => { e.preventDefault(); const file = fileInput.files[0]; if (!file) { alert("请先选择文件"); return; } // 校验文件格式 if (!file.name.match(ALLOWED_EXT)) { alert("请选择符合要求的文件格式"); return; } const formData = new FormData(); const reader = new FileReader(); reader.onload = function() { // 加密后把CipherParams转成字符串再封装成File const encrypted = CryptoJS.AES.encrypt(reader.result, ENCRYPT_KEY).toString(); // 可以保留原文件名加.encrypted后缀 方便后续解密识别 const encryptedFile = new File([encrypted], `${file.name}.encrypted`, { type: "text/plain" }); formData.append('files', encryptedFile); // 加密完成、append到formData后再发起请求 fetch(url, { method: 'POST', body: formData, }).then((response) => { console.log("上传成功"); }).catch(err => { console.error("上传失败", err); }); } // 如果需要加密原始二进制 可以用readAsArrayBuffer 再转成CryptoJS支持的WordArray // 这里保留你原来的readAsDataURL写法 如需修改可以自行调整 reader.readAsDataURL(file); });
补充说明
- 服务器端PHP接收逻辑需要正常处理
$_FILES['files'],同时确保上传目录有可写权限,否则即使收到文件也无法写入目录。 - 如果你需要加密文件原始二进制内容,可将
readAsDataURL改为readAsArrayBuffer,再通过CryptoJS.lib.WordArray.create(arrayBuffer)转换格式后再加密,体积更小更高效。 - 解密时需要先读取加密后的字符串,用
CryptoJS.AES.decrypt(加密字符串, ENCRYPT_KEY).toString(CryptoJS.enc.Utf8)拿到原始Data URI,再去掉前缀转成对应文件格式即可还原。
内容的提问来源于stack exchange,提问作者Svitlana Kuievda
相关产品推荐
相关产品推荐

