Итак, представьте, что у меня есть Scala Vert.x Web REST API, который получает загрузку файлов через составные HTTP-запросы. Однако он не получает входящие данные файла в виде одного файла InputStream
. Вместо этого каждый файл принимается как серия байтовых буферов, передаваемых через несколько функций обратного вызова.
Обратные вызовы в основном выглядят так:
// the callback that receives byte buffers (chunks) of the file being uploaded
// it is called multiple times until the full file has been received
upload.handler { buffer =>
// send chunk to backend
}
// the callback that gets called after the full file has been uploaded
// (i.e. after all chunks have been received)
upload.endHandler { _ =>
// do something after the file has been uploaded
}
// callback called if an exception is raised while receiving the file
upload.exceptionHandler { e =>
// do something to handle the exception
}
Теперь я хотел бы использовать эти обратные вызовы, чтобы сохранить файл в корзине MinIO (MinIO, если вы не знакомы, в основном представляет собой самостоятельный S3, и его API почти такой же, как S3 Java API).
Поскольку у меня нет дескриптора файла, мне нужно использовать putObject()
, чтобы поместить InputStream
в MinIO.
Неэффективный обходной путь, который я сейчас использую с MinIO Java API, выглядит следующим образом:
// this is all inside the context of handling a HTTP request
val out = new PipedOutputStream()
val in = new PipedInputStream()
var size = 0
in.connect(out)
upload.handler { buffer =>
s.write(buffer.getBytes)
size += buffer.length()
}
upload.endHandler { _ =>
minioClient.putObject(
PutObjectArgs.builder()
.bucket("my-bucket")
.object("my-filename")
.stream(in, size, 50000000)
.build())
}
Очевидно, что это не оптимально. Поскольку здесь я использую простой поток java.io
, весь файл загружается в память.
Я не хочу сохранять файл на диск на сервере, прежде чем помещать его в хранилище объектов. Я хотел бы поместить его прямо в свое хранилище объектов.
Как я могу добиться этого, используя S3 API и серию байтовых буферов, предоставленных мне через обратный вызов upload.handler
?
ИЗМЕНИТЬ
Я должен добавить, что я использую MinIO, потому что я не могу использовать коммерческое облачное решение, такое как S3. Однако, как упоминалось на веб-сайте MinIO, я могу использовать Amazon S3 Java SDK, используя MinIO в качестве решения для хранения данных.
Я попытался выполнить это руководство на веб-сайте Amazon для загрузки объектов. до S3 кусками.
Это решение, которое я попытался, выглядит так:
context.request.uploadHandler { upload =>
println(s"Filename: ${upload.filename()}")
val partETags = new util.ArrayList[PartETag]
val initRequest = new InitiateMultipartUploadRequest("docs", "my-filekey")
val initResponse = s3Client.initiateMultipartUpload(initRequest)
upload.handler { buffer =>
println("uploading part", buffer.length())
try {
val request = new UploadPartRequest()
.withBucketName("docs")
.withKey("my-filekey")
.withPartSize(buffer.length())
.withUploadId(initResponse.getUploadId)
.withInputStream(new ByteArrayInputStream(buffer.getBytes()))
val uploadResult = s3Client.uploadPart(request)
partETags.add(uploadResult.getPartETag)
} catch {
case e: Exception => println("Exception raised: ", e)
}
}
// this gets called for EACH uploaded file sequentially
upload.endHandler { _ =>
// upload successful
println("done uploading")
try {
val compRequest = new CompleteMultipartUploadRequest("docs", "my-filekey", initResponse.getUploadId, partETags)
s3Client.completeMultipartUpload(compRequest)
} catch {
case e: Exception => println("Exception raised: ", e)
}
context.response.setStatusCode(200).end("Uploaded")
}
upload.exceptionHandler { e =>
// handle the exception
println("exception thrown", e)
}
}
}
Это работает для небольших файлов (мой тестовый маленький файл был 11 байт), но не для больших файлов.
В случае больших файлов процессы внутри upload.handler
постепенно замедляются по мере того, как файл продолжает загружаться. Кроме того, upload.endHandler
никогда не вызывается, и файл почему-то продолжает загружаться после того, как 100% файла были загружены.
Однако, как только я закомментирую часть s3Client.uploadPart(request)
внутри upload.handler
и части s3Client.completeMultipartUpload
внутри upload.endHandler
(фактически выбрасывая файл, а не сохраняя его в хранилище объектов), загрузка файла проходит как обычно и завершается корректно.