【发布时间】:2022-01-15 13:10:47
【问题描述】:
我查看了几篇 SO 帖子,试图找到一种方法,让 Node.js 服务器告诉客户端在达到特定文件大小后停止上传。其中最有前途的是ed-taAvoiding further processing on busyboy file upload size limit 的技术。
根据 ed-ta,我的 Node.js 服务器似乎在做它应该做的事情。一旦达到大小限制,服务器就会发送 455 状态代码并停止接受更多数据。不幸的是,我的客户一直在处理文件,直到它完全完成为止。当用户尝试上传非常大的文件时,这是一种不太理想的体验,因为在 AJAX 请求完全完成之前,客户端不会提醒用户已达到阈值。
如何让客户端及时看到455状态码?
我尝试检查 xhr.onreadystatechange 中的 455 状态,但我似乎无法从 onreadystatehange 中找到该信息,即使服务器已经在响应 1 中发送了 455。此外,onreadystatechange 事件似乎直到在整个文件已被客户端处理后才触发。
我试图通过删除不相关的细节来简化问题,我当前的演示代码如下:
Server.js
// This code is based on
// https://stackoverflow.com/questions/23691194/node-express-file-upload
//
// [1] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [2] - https://stackoverflow.com/questions/18310394/
// no-access-control-allow-origin-node-apache-port-issue
//
// [3] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [4] - https://stackoverflow.com/questions/44736327/
// node-js-cors-issue-response-to-preflight-
// request-doesnt-pass-access-control-c
var express = require('express');
var busboy = require('connect-busboy');
var fs = require('fs-extra');
var cors = require('cors'); // [4]
const app = express();
// See [2][4]
app.use(
function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "null");
res.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST")
next();
}
);
app.options('*', cors());
app.use(
busboy({ limits: { files: 1, fileSize: 500000000 } }) // [1]
);
app.post('/uploadEndpoint', function (req, res, next) {
var fStream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldName, file, filename) {
console.log("Uploading: " + filename);
var destPath = './' + filename;
fStream = fs.createWriteStream(destPath);
file.pipe(fStream);
// ed-ta [3]
// Despite being asynchronous limit_reach
// will be seen here as true if it hits max size
// as set in file.on.limit because once it hits
// max size the stream will stop and on.finish
// will be triggered.
var limit_reach = false;
req.busboy.on('finish', function() {
if(!limit_reach){
res.send(filename + " uploaded");
}
});
file.on('limit', function() {
fs.unlink(destPath, function(){
limit_reach = true;
res.status(455).send("File too big.");
console.log('Telling client to stop...');
});
});
});
})
app.listen(8000);
test.html
<!DOCTYPE html>
<!--
This code is based off timdream and Basar at
https://stackoverflow.com/questions/6211145/
upload-file-with-ajax-xmlhttprequest
[1] - https://stackoverflow.com/questions/49692745/
express-using-multer-error-multipart-boundary-not-found-request-sent-by-pos
-->
<html>
<head>
<meta charset="utf-8" />
<script>
function uploadFile() {
var progress = document.getElementById("output");
progress.innerText = "Starting";
var fileCtl = document.getElementById("theFile");
var file = fileCtl.files[0];
var xhr = new XMLHttpRequest();
// timdream
var formData = new FormData();
formData.append('theFile', file);
xhr.upload.onprogress = (progressEvent) => {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
progress.innerText =
"Percent Uploaded: " + percentCompleted + "%";
};
xhr.onreadystatechange = function(e) {
if (this.status === 455) {
alert('Sorry, file was too big.');
}
};
xhr.open('post', 'http://localhost:8000/uploadEndpoint', true);
// Apparently this line causes issues Multipart Boundary not
// found error [1]
// xhr.setRequestHeader("Content-Type","multipart/form-data");
// timdream
xhr.send(formData);
}
</script>
</head>
<body>
<input type="file" id="theFile" name="theName" /><br />
<div id="output">Upload Progress</div>
<input type="button" id="theButton"
onclick="uploadFile();" value="Send" />
</body>
</html>
1 - 我可以在 Node.js 服务器上设置另一个端点,并使用 AJAX 轮询该端点以了解客户端 onprogress 中的当前状态,但这似乎是一种浪费带宽的笨拙解决方案。
【问题讨论】:
-
到目前为止,这是我找到的唯一可能的解决方案:npmjs.com/package/socketio-file-upload 不幸的是,我无法在客户端及时触发 XMLHttpRequest 中止、错误和 onreadystatechange 事件...遗憾的是,这条评论实际上并没有回答这个问题,因为从 socketio-file-upload 包上传的 WebSocket 与 multipart/form-data 帖子不同。
-
我刚刚尝试将我的客户端切换到 Axios 并使用取消令牌。即使我调用 cancelTokenSource.cancel(),我的客户也会继续。这让 no 有意义... stackoverflow.com/questions/38329209/… stackoverflow.com/questions/55051917/…
-
回到 XMLHttpRequest。为服务器打开了一个单独的 WebSockets 通道。当服务器通过 WebSockets 通道发回中止请求时,我的客户端能够在实例化的 XMLHttpRequest 对象上调用中止:developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort
标签: node.js ajax express xmlhttprequest busboy