You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus文件上传接口RestAssured测试报错:无法访问上传文件

Quarkus文件上传接口NoSuchFileException问题排查

问题场景

我基于Quarkus实现了一个PUT接口,用于接收文件并存储到指定路径,接口代码如下:

@Path("upload")
@PUT
public void uploadTagFile(@RestForm("tagFile") FileUpload tagFile, @HeaderParam("sessionId") UUID sessionId) {
    Session session = sessionService.obtainActiveSession(sessionId);

    File copied;
    try {
        copied = Files.copy(tagFile.uploadedFile(), java.nio.file.Path.of(storageDirectory + "/sessions/" + tagFile.fileName())).toFile();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    tagFilesService.addTagFile(session, copied);
}

同时用RestAssured编写了测试用例,上传测试模块resources目录下的文件:

@Test
public void uploadTagFile_ValidSessionWithFile_UploadSuccessful() {
    //Setup
    File file = ResourceUtils.getResourceAsFile("sampledata/tag/unitdata.tag");
    Assertions.assertNotNull(file);

    //Action + Assert
    given().header("sessionId", sessionId)
            .header(new Header("content-type", "multipart/form-data"))
            .multiPart("tagFile", file)
            .contentType("multipart/form-data")
            .when()
            .put("/api/tag/upload")
            .then()
            .statusCode(204);
}

错误现象

测试运行时抛出NoSuchFileException,完整报错日志如下:

2023-11-07 16:15:27,918 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /api/tag/upload failed, error id: 2dc30e07-b49c-4155-817a-3599e53af9b5-1: java.lang.RuntimeException: java.nio.file.NoSuchFileException: C:\Users\XXX\AppData\Local\Temp\uploads\resteasy-reactive5082349700154976184upload -> tmpstorage\sessions\7577918012706382072813560817.tmp
    at com.xxx.tpu.quarkus.tag.TagFilesController.uploadTagFile(TagFilesController.java:40)
    at com.xxx.tpu.quarkus.tag.TagFilesController$quarkusrestinvoker$uploadTagFile_f34cf665f31482fa40b85501ce781413d7f28f10.invoke(Unknown Source)
    at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
    at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.nio.file.NoSuchFileException: C:\Users\XXX\AppData\Local\Temp\uploads\resteasy-reactive5082349700154976184upload -> tmpstorage\sessions\7577918012706382072813560817.tmp
    at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
    at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
    at java.base/sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:202)
    at java.base/sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:283)
    at java.base/java.nio.file.Files.copy(Files.java:1295)
    at com.xxx.tpu.quarkus.tag.TagFilesController.uploadTagFile(TagFilesController.java:38)
    ... 11 more

调试后确认文件已成功传输,但无法通过tagFile.uploadedFile()访问该文件,请问哪里操作有误?

问题原因及解决方法

1. 目标父目录未创建

Files.copy方法默认不会自动创建目标路径的父目录,如果tmpstorage/sessions目录不存在,就会抛出NoSuchFileException。解决方法是在复制前先递归创建所有不存在的父目录:

Path targetPath = Path.of(storageDirectory + "/sessions/" + tagFile.fileName());
Files.createDirectories(targetPath.getParent());
copied = Files.copy(tagFile.uploadedFile(), targetPath).toFile();

2. Reactive模式下临时文件生命周期问题

在Quarkus的Resteasy Reactive模式中,uploadedFile()返回的临时文件可能在你尝试访问前就被框架清理了,更可靠的方式是通过输入流读取文件内容:

Path targetPath = Path.of(storageDirectory + "/sessions/" + tagFile.fileName());
Files.createDirectories(targetPath.getParent());
try (InputStream inputStream = Files.newInputStream(tagFile.uploadedFile())) {
    Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
copied = targetPath.toFile();

3. 测试用例冗余配置优化

RestAssured中使用multiPart时会自动设置正确的Content-Type头,无需重复手动添加,简化后的测试代码如下:

@Test
public void uploadTagFile_ValidSessionWithFile_UploadSuccessful() {
    File file = ResourceUtils.getResourceAsFile("sampledata/tag/unitdata.tag");
    Assertions.assertNotNull(file);

    given()
        .header("sessionId", sessionId)
        .multiPart("tagFile", file)
    .when()
        .put("/api/tag/upload")
    .then()
        .statusCode(204);
}

4. 存储路径合法性检查

确认storageDirectory配置的路径符合预期:

  • 如果是相对路径,检查运行环境的工作目录是否和预期一致
  • 确保当前进程拥有该路径的读写权限

内容的提问来源于stack exchange,提问作者monamona

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:27:32