云
10 小时以前 7db504cbc6d08e50666c86a3dbd6c7da5147b630
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/qrexport/MesProQrExportTaskServiceImpl.java
@@ -15,6 +15,7 @@
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
import java.io.BufferedInputStream;
import java.io.File;
@@ -193,6 +194,7 @@
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
            }
            response.setContentLengthLong(length);
            long sent = 0;
            try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
                in.skipNBytes(start);
                OutputStream out = response.getOutputStream();
@@ -205,8 +207,19 @@
                    }
                    out.write(buffer, 0, read);
                    remaining -= read;
                    sent += read;
                }
                out.flush();
            } catch (AsyncRequestNotUsableException e) {
                // 浏览器取消、网络断开或代理关闭连接时,响应已经不可写,不能再返回 JSON 错误。
                // ZIP 保留到清理时间,前端可重试当前分段;记录范围与进度便于排查频繁断开。
                log.warn("[download][客户端中断 taskId={}, range={}, sent={}/{} bytes: {}]",
                        id, range, sent, length, e.getMessage());
            } catch (IOException e) {
                // ZIP 响应头已经写入,不能交给全局异常处理器序列化 CommonResult。
                // Content-Length 不匹配会让客户端判定本段失败并重试。
                log.error("[download][传输失败 taskId={}, range={}, sent={}/{} bytes]",
                        id, range, sent, length, e);
            }
        } finally {
            activeDownloads.computeIfPresent(id, (key, count) -> count > 1 ? count - 1 : null);