【发布时间】:2021-01-20 20:48:59
【问题描述】:
我正在为我的 Messenger 进行文件加密,但在加密完成后我正在努力上传文件。
加密在性能方面看起来不错,但是当我尝试上传时,浏览器完全挂起。 Profiler 无限写入“small GC”事件,每 10 秒出现一次挂起脚本的黄条。
我已经尝试过的:
-
使用 FileReader 将文件读取到 ArrayBuffer,然后将其转换为基本 Array,对其进行加密,然后创建一个 FormData 对象,从数据中创建一个 File,将其附加到 FormData 并发送。当我没有进行加密时,它可以快速处理大小约为 1.3 Mb 的原始、未触及的文件,但在上传后加密的“假”文件对象上,我得到了 4.7 Mb 的文件并且它不可用。
-
作为普通 POST 字段发送(多部分表单数据编码)。以这种方式保存在PHP上后数据已损坏。
-
作为 Base64 编码的 POST 字段发送。最后,在我找到从二进制数组到 Base64 字符串的快速转换函数后,它开始以这种方式工作。 btoa() 在编码/解码后给出了错误的结果。但是在我尝试了一个 8.5 Mb 大小的文件后,它又挂了。
-
我尝试将额外数据移动到 URL 字符串并将文件作为 Blob 发送,如 here 所述。没有成功,浏览器仍然挂起。
-
我尝试向 Blob 构造函数传递一个基本数组,一个由它组成的 Uint8Array,最后我尝试按照文档中的建议发送文件,但结果仍然相同,即使是小文件。
代码有什么问题?发生此挂起时,HDD 负载为 0%。有问题的文件也非常小
当我按下按钮紧急终止 JS 脚本时,服务器脚本的输出如下所示:
Warning: Unknown: POST Content-Length of 22146226 bytes exceeds the limit of 8388608 bytes in Unknown on line 0
Warning: Cannot modify header information - headers already sent in Unknown on line 0
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent in D:\xmessenger\upload.php on line 2
Array ( )
这是我的 JavaScript:
function uploadEncryptedFile(nonce) {
if (typeof window['FormData'] !== 'function' || typeof window['File'] !== 'function') return
var file_input = document.getElementById('attachment')
if (!file_input.files.length) return
var file = file_input.files[0]
var reader = new FileReader();
reader.addEventListener('load', function() {
var data = Array.from(new Uint8Array(reader.result))
var encrypted = encryptFile(data, nonce)
//return //Here it never hangs
var form_data = new FormData()
form_data.append('name', file.name)
form_data.append('type', file.type)
form_data.append('attachment', arrayBufferToBase64(encrypted))
/* form_data.append('attachment', btoa(encrypted)) // Does not help */
form_data.append('nonce', nonce)
var req = getXmlHttp()
req.open('POST', 'upload.php?attachencryptedfile', true)
req.onload = function() {
var data = req.responseText.split(':')
document.getElementById('filelist').lastChild.realName = data[2]
document.getElementById('progress2').style.display = 'none'
document.getElementById('attachment').onclick = null
encryptFilename(data[0], data[1], data[2])
}
req.send(form_data)
/* These lines also fail when the file is larger */
/* req.send(new Blob(encrypted)) */
/* req.send(new Blob(new Uint8Array(encrypted))) */
})
reader.readAsArrayBuffer(file)
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
这是我的 PHP 处理程序代码:
if (isset($_GET['attachencryptedfile'])) {
$entityBody = file_get_contents('php://input');
if ($entityBody == '') exit(print_r($_POST, true));
else exit($entityBody);
if (!isset($_POST["name"])) exit("Error");
$name = @preg_replace("/[^0-9A-Za-z._-]/", "", $_POST["name"]);
$nonce = @preg_replace("/[^0-9A-Za-z+\\/]/", "", $_POST["nonce"]);
if ($name == ".htaccess") exit();
$data = base64_decode($_POST["attachment"]);
//print_r($_POST);
//exit();
if (strlen($data)>1024*15*1024) exit('<script type="text/javascript">parent.showInfo("Файл слишком большой"); parent.document.getElementById(\'filelist\').removeChild(parent.document.getElementById(\'filelist\').lastChild); parent.document.getElementById(\'progress2\').style.display = \'none\'; parent.document.getElementById(\'attachment\').onclick = null</script>');
$uname = uniqid()."_".str_pad($_SESSION['xm_user_id'], 6, "0", STR_PAD_LEFT).substr($name, strrpos($name, "."));
file_put_contents("upload/".$uname, $data);
mysql_query("ALTER TABLE `attachments` AUTO_INCREMENT=0");
mysql_query("INSERT INTO `attachments` VALUES('0', '".$uname."', '".$name."', '0', '".$nonce."')");
exit(mysql_insert_id().":".$uname.":".$name);
}
HTML 表单:
<form name="fileForm" id="fileForm" method="post" enctype="multipart/form-data" action="upload.php?attachfile" target="ifr">
<div id="fileButton" title="Прикрепить файл" onclick="document.getElementById('attachment').click()"></div>
<input type="file" name="attachment" id="attachment" title="Прикрепить файл" onchange="addFile()" />
</form>
【问题讨论】:
-
也许你应该显示一些代码......
-
在后端加密然后保存呢?
-
不幸的是我不能。信使的整个想法是E2E。我需要像 Telegram 这样的东西(他们用文件正确实现了它)
标签: javascript ajax file xmlhttprequest