如何检测空的多部分数据传输
Posted
技术标签:
【中文标题】如何检测空的多部分数据传输【英文标题】:How to detect an empty multipart data transmission 【发布时间】:2019-06-09 17:16:15 【问题描述】:所以我编写了一个小 servlet 来测试文件上传。
用于触发上传的表单很简单:
<form method="post" action="/webapp/upload" enctype="multipart/form-data">
Choose the file(s) to upload:<br>
<input type="file" name="files" multiple/>
<input type="submit" value="Upload" />
</form>
相关的servlet功能结构可以概括为
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
PrintWriter out = resp.getWriter();
resp.setContentType("text/html");
req.getParts().parallelStream().forEach(p ->
//store uploaded file(s) to disk and communicate success/failure to client
);
req.getRequestDispatcher(uploadForm).include(req,resp);
但是,我在没有选择任何文件的情况下不小心点击了上传按钮,这就是返回给我的内容。
uploading '': application/octet-stream, 0KB...OK
确实如此,服务器上的上传文件夹现在包含一个名为 (1)
的文件(因为我的重命名方法决定了空名称已经存在),它是 0
字节大。
这让我意识到我不知道如何检查“没有选择文件”。
在这件该死的事情上花了足够的时间,我不妨把它备份到 *** 上以供以后参考。谁知道呢,也许其他人也觉得它有用。或者有人有有用的评论。
【问题讨论】:
【参考方案1】:我不想检查文件大小,因为文件可能恰好是 0 字节大小(在大多数情况下,这是一个无用的文件,诚然,但仍然如此)。检查空文件名只能让我决定是否可以丢弃特定的Part
。
如果可以避免,我也不想多次迭代这些部分。
所以我想我能做的就是创建一个布尔标志,如果我最终上传任何内容,我将其设置为 true ...
boolean uploadedAnything = false;
由于我使用的是parallelStream
,因此更新此标志将需要一些同步。我已经在out
流上同步了,为什么不直接把它扔进去呢?
synchronized (out)
out.print(String.format("uploading '%s': %s, %d%s...", fileName, p.getContentType(), sizeInKb, "KB"));
if (success)
out.println("OK<br>");
uploadedAnything = true;
else out.println("FAILED<br>");
除了 Java 拒绝编译它,因为
从 lambda 表达式引用的局部变量必须是 final 或有效 final
这与synchronized
块无关,但更重要的是整个事物都封装在一个
boolean uploadedAnything = false;
req.getParts().parallelStream().forEach(p ->
//`uploadedAnything` gets changed here
);
我想用AtomicBoolean
替换boolean
可能会起作用,但我不太喜欢这种解决方案,因为我讨厌添加我知道没有用的同步。
所以...下一个想法:
与其选择forEach
,不如选择map
。这样,我们将 Part
s 列表变成了一个语句列表,无论上传与该 Part
对应的文件是否实际成功。
即
boolean uploadedAnything = req.getParts().parallelStream().map(p ->
[...]
return success;
).matchAny(Predicate.isEqual(true));
除了我们也不能这样做,因为matchAny
然后会短路,无法处理所有文件。哎呀。
所以...
List<Boolean> uploadStatus = req.getParts().parallelStream().map(p ->
[...]
return success;
).collect(Collectors.toList());
boolean uploadedAnything = uploadStatus.stream().anyMatch(Predicate.isEqual(true));
...好吧,这行得通,但现在我创建了一个完整的布尔值列表,只是为了得到一个布尔值。
我想我们可以弃牌...
boolean uploadedAnything = req.getParts().parallelStream().fold(false,(status,p) ->
[...]
return status || success;
);
... 除了 Java 不支持折叠之外,还有一个与功能有些相似的东西需要第三个参数,即“组合器”。因此,我们必须在网络上搜索以找出该组合器实际发挥作用的原因和方式。
找到https://***.com/a/24316429/3322533,sn-p就变成了
boolean uploadedAnything = req.getParts().parallelStream().reduce(false,(status,p) ->
[...]
return status || success;
,(accA, accB) -> accA || accB);
我们可以重写
boolean uploadedAnything = req.getParts().parallelStream().reduce(false,(status,p) ->
[...]
return status || success;
,Boolean::logicalOr);
这仍然不是折叠,因为它对操作顺序造成严重破坏(幸运的是,在这种情况下这无关紧要),但它完成了工作。
【讨论】:
以上是关于如何检测空的多部分数据传输的主要内容,如果未能解决你的问题,请参考以下文章