我需要将图像上传到服务器,并且这样做我不能使用其他库,而是将其(以base64编码)拆分为块并全部上传。
我正在使用Kotlin协程Flows,而我目前正在做的是第一个调用(返回流)以获得需要在所有上传请求中附加的图片ID
这是我用来上传图片的2个功能
fun submitImage(payload: Payload): Flow<String> {
val request = requestBuilder.buildUploadImageRequest(payload)
return client.execute(request)
.serviceFlow({ response ->
val imageId = response.body.id
uploadImage(payload.imageBase64, imageId)
imageId
}, { response ->
throw MyServerError("Error ${response.error}")
})
}
private fun uploadImage(imageBase64: String, imageId: String) {
val chunks = divideEncodedImageInChunksOfSize(imageBase64)
var v = 1
for (chunk in chunks) {
val payload = generatePayload(imageId, v, chunk, false)
submitImageChunk(payload)
v++
}
val payload = generatePayload(imageId, v, "", true)
submitImageChunk(payload)
}
private fun submitImageChunk(payload: JSONObject): Flow<Unit> {
val request = requestBuilder.buildUploadImageChunkRequest(payload)
return client.execute(request)
.serviceFlow({ }, { response ->
throw MyHttpError(response)
})
}
我利用以下实用程序功能
// Extension function to handle Flows and their activation
internal fun MyHttpClient.execute(request: MyHttpRequest): Flow<MyHttpResponse> {
return flow {
val deferred = CompletableDeferred<MyHttpResponse>()
executeHttp(request, object : MyHttpListener {
override fun onSuccess(response: MyHttpResponse) {
deferred.complete(response)
}
override fun onFailure(response: MyHttpResponse) {
deferred.completeExceptionally(MyHttpError(response))
}
})
emit(deferred.await())
}
}
// Extension function to catch exceptions AND to check if the response body is null
internal fun <T> Flow<MyHttpResponse>.serviceFlow(
onSuccess: (response: MyHttpResponse) -> T,
onError: (response: MyHttpResponse) -> Unit
) = flatMapConcat { response ->
flowOf(response)
.map { res ->
res.body?.let { it ->
onSuccess(res)
} ?: throw MyParseError("MyHttpResponse has a null body")
}
.catchException<JSONException, T> { e ->
throw MyParseError("Parsing exception $e")
}
}.catchException<MyHttpError, T> { e ->
onError(e.response)
}
// Function leveraging OkHttpClient to make a HTTPRequest
internal fun executeHttp { ... }
我认为问题是由于以下事实:函数submitImage
在启动所有子流以上传图像后返回,但它并不等待所有子流完成。
我不确定Kotlin协程对于这样的用例有什么构造,有人可以帮助我吗?
答案 0 :(得分:0)
我认为您应该使用WorkManager并考虑使用chain woker feature。
具有Flow功能,请尝试以下操作:
private suspend fun uploadImage(imageBase64: String, imageId: String) {
withContext(Dispatchers.IO){
val chunks = divideEncodedImageInChunksOfSize(imageBase64)
var v = 1
for (chunk in chunks) {
val payload = generatePayload(imageId, v, chunk, false)
submitImageChunk(payload)
v++
}
val payload = generatePayload(imageId, v, "", true)
submitImageChunk(payload).await();
}
private suspend fun submitImageChunk(payload: JSONObject): Deferred<Unit> {
val request = requestBuilder.buildUploadImageChunkRequest(payload)
return client.execute(request);
}
答案 1 :(得分:0)
谢谢musafee使我朝着正确的方向前进。
最后的答案是,我在router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name,
};
Company.updateOne(
{ _id: companyId },
{
$push: {
board_posts: boardPost,
},
}
)
.then((result) => {
console.log(result);
// result.n; // Number of documents matched
// result.nModified; // Number of documents modified
res.send(result);
})
.catch((error) => {
console.log(error);
res.status(500);
});
});
函数中创建了这些流,但实际上我从未在它们上调用uploadImage
,因此它们仍然没有启动。
我选择的解决方案是将在那里创建的流的列表返回给调用函数,并从那里将collect
函数的返回类型从submitImage
更改为{{1 }},并从上层触发它们