Hi,
I have implemented HTTP server for the multipart form using BodyHandler.create() with Router.
But as I understand BodyHandlerImpl class, first, the uploaded file should be save to the upload directory
after that, from the routing context like ctx.fileUploads(), the uploaded file can be read from the upload directory.
I want to avoid such saving uploaded file data into the directory,
Without saving uploaded file to the directory, I just want to read the uploaded file data directly which should be sent to kafka afterwards.
I have thought that for my case, performance issue could occur with this approach like saving uploaded file data into directory first.
Thus I have implemented a custom BodyHandler which is similar to BodyHandlerImpl except saving uploaded file data to directory.
It works good for me for now,
but I am not sure, it is a correct way or there is another alternative way to solve my problem.
Thanks,
- Kidong Lee.
P.S: I have added some codes from my custome body handler.
The following listing is BHandler class in my custom MultipartBodyHandlerImpl class, it is very similar to the original BHandler in BodyHandlerImpl:
....
public BHandler(RoutingContext context, long contentLength) {
this.context = context;
Set<FileUpload> fileUploads = context.fileUploads();
final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE);
if (contentType == null) {
isMultipart = false;
isUrlEncoded = false;
} else {
final String lowerCaseContentType = contentType.toLowerCase();
isMultipart = lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString());
isUrlEncoded = lowerCaseContentType.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString());
}
initBodyBuffer(contentLength);
if (isMultipart || isUrlEncoded) {
context.request().setExpectMultipart(true);
context.request().uploadHandler(upload -> {
if (bodyLimit != -1 && upload.isSizeAvailable()) {
// we can try to abort even before the upload starts
long size = uploadSize + upload.size();
if (size > bodyLimit) {
failed = true;
context.fail(413);
return;
}
}
if (handleFileUploads) {
// we actually upload to a file with a generated filename
uploadCount.incrementAndGet();
// NOTE: upload buffer stream will not be saved to the upload directory.
// buffer data will be set to MultipartFileUploadImpl instance which will be forwarded through Routing Context.
Pump p = Pump.pump(upload, new UploadWriteStream(fileUploads, upload));
p.start();
upload.resume();
upload.exceptionHandler(t -> {
deleteFileUploads();
context.fail(t);
});
upload.endHandler(v -> uploadEnded());
}
});
}
context.request().exceptionHandler(t -> {
deleteFileUploads();
context.fail(t);
});
}
private class UploadWriteStream implements WriteStream<Buffer>
{
private Set<FileUpload> fileUploads;
private HttpServerFileUpload upload;
public UploadWriteStream(Set<FileUpload> fileUploads, HttpServerFileUpload upload)
{
this.fileUploads = fileUploads;
this.upload = upload;
}
public WriteStream<Buffer> exceptionHandler(Handler<Throwable> handler) {
return this;
}
public WriteStream<Buffer> write(Buffer buffer) {
byte[] data = buffer.getBytes();
// Here, MultipartFileUploadImpl containing uploaded file data bytes will be added to Set<FileUpload> of routing context
MultipartFileUploadImpl fileUpload = new MultipartFileUploadImpl(null, upload, data);
fileUploads.add(fileUpload);
return this;
}
public void end() {
}
public WriteStream<Buffer> setWriteQueueMaxSize(int maxSize) {
return this;
}
public boolean writeQueueFull() {
return false;
}
public WriteStream<Buffer> drainHandler(Handler<Void> handler) {
return this;
}
}
..........
public class MultipartFileUploadImpl implements FileUpload {
private final String uploadedFileName;
private final HttpServerFileUpload upload;
private final byte[] data;
public MultipartFileUploadImpl(String uploadedFileName, HttpServerFileUpload upload, byte[] data) {
this.uploadedFileName = uploadedFileName;
this.upload = upload;
this.data = data;
}
public byte[] getData() {
return data;
}
public String name() {
return this.upload.name();
}
public String uploadedFileName() {
return this.uploadedFileName;
}
public String fileName() {
return this.upload.filename();
}
public long size() {
return this.upload.size();
}
public String contentType() {
return this.upload.contentType();
}
public String contentTransferEncoding() {
return this.upload.contentTransferEncoding();
}
public String charSet() {
return this.upload.charset();
}
}
.........