所以我写了一个小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
个字节。
这使我意识到我不知道如何检查“未选择文件”。
花了足够的时间在这件事上,所以我最好将它备份在stackoverflow上以供以后参考。谁知道,也许其他人也觉得它有用。或有人有帮助的评论。
答案 0 :(得分:0)
我不想检查文件的大小,因为一个文件的大小可能恰好是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表达式引用的局部变量必须是最终的或实际上是最终的
与synchronized
块无关,而更重要的是整个内容都封装在
boolean uploadedAnything = false;
req.getParts().parallelStream().forEach(p -> {
//`uploadedAnything` gets changed here
});
我想用boolean
代替AtomicBoolean
是可行的,但是我不太喜欢那种解决方案,因为我讨厌添加我不知道没有用的同步。
所以...下一个想法:
让我们去forEach
,而不是去map
。这样,无论上传Part
的文件是否成功,我们都将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不支持fold之外,与某种相似功能相似的一件事需要第三个参数“ Combiner”。因此,我们必须在网上进行搜索,以找出该组合器真正发挥作用的原因和方式。
找到了https://stackoverflow.com/a/24316429/3322533,该摘录变成了
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);
这个STILL并不是折衷的,因为它对操作顺序造成了严重破坏(幸运的是,在这种情况下这无关紧要),但是它可以完成工作。