Я не хочу проверять размер файла, потому что файл МОЖЕТ быть размером 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 отказывается компилировать это, потому что
локальные переменные, на которые ссылается лямбда-выражение, должны быть окончательными или эффективно окончательными
, который не имеет ничего общего с блоком synchronized
, но больше относится к тому факту, что все это заключено в
boolean uploadedAnything = false;
req.getParts().parallelStream().forEach(p -> {
//`uploadedAnything` gets changed here
});
Полагаю, замена boolean
на AtomicBoolean
может сработать, но мне это решение не очень нравится, потому что я ненавижу добавлять синхронизацию, которая, как я ЗНАЮ, бесполезна.
Итак ... следующая идея:
Вместо того, чтобы идти на forEach
, давайте на map
. Таким образом, мы превращаем наш список Part
в список операторов независимо от того, действительно ли загрузка файла, соответствующего этому Part
, действительно удалась.
1036 * Т.е. *
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 не поддерживает сгибы, и одна вещь, которая приближается к несколько похожей функциональности, требует третьего аргумента, "Combiner". Поэтому нам нужно поискать в Интернете, чтобы выяснить, почему и как этот 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 не складывается, потому что он наносит ущерб порядку операций (что, к счастью, в данном случае не имеет значения), но он выполняет свою работу.