Vert.x Web (Scala):从路由器读取分段文件上传为流而不保存到磁盘
Vert.x Web (Scala): Read Multipart File Upload as Stream from Router without Saving to Disk
我知道可以通过 Vert.x Web Router
处理文件上传,只需使用像这样的东西:
myRouter.post("/upload")
.handler(BodyHandler.create("myUploadDirectory")))
.handler { context =>
// do something with the uploaded files and respond to the request
}
但是,这会将文件保存在本地服务器上(甚至可能是网络共享)。临时缓冲磁盘上的小文件并将它们批量移动到另一个存储可能完全没问题,但对于非常大的文件(例如几千兆字节)就不能这样说了。
什么是读取文件上传作为字节流的好方法,例如,并将其直接写入最终存储,然后能够优雅地处理失败和成功,全部来自 Router
?
代理上传这将避免让客户公开访问商店,并且可能允许对上传过程进行更细粒度的控制,而不仅仅是在服务器上创建本地文件或公开 object/blob 商店。
编辑:
我知道另一种方法是做这样的事情,在用我的 Router
:
处理请求之前将文件上传作为一种特殊情况处理
val myHttpServer = myVertx.createHttpServer()
myHttpServer.requestHandler(request => {
if(request.absoluteURI().contains("/upload")) {
request.setExpectMultipart(true)
request.handler { buffer =>
// upload part of the file
}
request.endHandler { end =>
// perform some action when the upload is done
}
} else
myRouter.handle(request)
})
但是,如您所见,它看起来很乱。用 Router.post()
或类似的东西处理它会更干净。
我是不是做错了什么?
我已尝试执行以下操作但无济于事(我只收到 HTTP 500,日志中没有有用的错误)。甚至 exceptionHandler
都没有被触发。
myRouter.post("/upload")
.handler { context =>
context.request.setExpectMultipart(true)
context.request.uploadHandler { upload =>
upload.handler { buffer =>
// send chunk to backend
}
upload.endHandler { _ =>
// upload successful
}
upload.exceptionHandler { e =>
// handle the exception
}
}
}
解决方案:
原来我在添加路由之前在 Router
中添加了 BodyHandler
。这是因为我希望能够在其他 POST 请求中接收到 JSON 正文,并且不想在接收到 JSON 的每个路由之前都输入 .handler(BodyHandler.create())
.
然而,正如 class 的名称所述... body 然后 handled,这意味着我将无法将 UploadHandler
添加到请求中。
我不熟悉 Scala,但这里有一个 Java 的解决方案:
router.post("/upload").handler(rc -> {
HttpServerRequest req = rc.request();
req.setExpectMultipart(true);
req.uploadHandler(upload -> {
upload.exceptionHandler(cause -> {
req.response().setChunked(true).end("Upload failed");
});
upload.endHandler(v -> {
req.response().setChunked(true).end("Successfully uploaded file");
});
upload.handler(buffer -> {
// Send to backend
});
});
});
我知道可以通过 Vert.x Web Router
处理文件上传,只需使用像这样的东西:
myRouter.post("/upload")
.handler(BodyHandler.create("myUploadDirectory")))
.handler { context =>
// do something with the uploaded files and respond to the request
}
但是,这会将文件保存在本地服务器上(甚至可能是网络共享)。临时缓冲磁盘上的小文件并将它们批量移动到另一个存储可能完全没问题,但对于非常大的文件(例如几千兆字节)就不能这样说了。
什么是读取文件上传作为字节流的好方法,例如,并将其直接写入最终存储,然后能够优雅地处理失败和成功,全部来自 Router
?
代理上传这将避免让客户公开访问商店,并且可能允许对上传过程进行更细粒度的控制,而不仅仅是在服务器上创建本地文件或公开 object/blob 商店。
编辑:
我知道另一种方法是做这样的事情,在用我的 Router
:
val myHttpServer = myVertx.createHttpServer()
myHttpServer.requestHandler(request => {
if(request.absoluteURI().contains("/upload")) {
request.setExpectMultipart(true)
request.handler { buffer =>
// upload part of the file
}
request.endHandler { end =>
// perform some action when the upload is done
}
} else
myRouter.handle(request)
})
但是,如您所见,它看起来很乱。用 Router.post()
或类似的东西处理它会更干净。
我是不是做错了什么?
我已尝试执行以下操作但无济于事(我只收到 HTTP 500,日志中没有有用的错误)。甚至 exceptionHandler
都没有被触发。
myRouter.post("/upload")
.handler { context =>
context.request.setExpectMultipart(true)
context.request.uploadHandler { upload =>
upload.handler { buffer =>
// send chunk to backend
}
upload.endHandler { _ =>
// upload successful
}
upload.exceptionHandler { e =>
// handle the exception
}
}
}
解决方案:
原来我在添加路由之前在 Router
中添加了 BodyHandler
。这是因为我希望能够在其他 POST 请求中接收到 JSON 正文,并且不想在接收到 JSON 的每个路由之前都输入 .handler(BodyHandler.create())
.
然而,正如 class 的名称所述... body 然后 handled,这意味着我将无法将 UploadHandler
添加到请求中。
我不熟悉 Scala,但这里有一个 Java 的解决方案:
router.post("/upload").handler(rc -> {
HttpServerRequest req = rc.request();
req.setExpectMultipart(true);
req.uploadHandler(upload -> {
upload.exceptionHandler(cause -> {
req.response().setChunked(true).end("Upload failed");
});
upload.endHandler(v -> {
req.response().setChunked(true).end("Successfully uploaded file");
});
upload.handler(buffer -> {
// Send to backend
});
});
});