| 1401 | } |
| 1402 | |
| 1403 | int OssClient::complete_multipart_upload(void* context, |
| 1404 | ObjectUploadOptions& opts) { |
| 1405 | assert(context); |
| 1406 | |
| 1407 | oss_multipart_context* ctx = (oss_multipart_context*)context; |
| 1408 | assert(!ctx->upload_id.empty()); |
| 1409 | |
| 1410 | estring req_body; |
| 1411 | req_body.appends("<CompleteMultipartUpload>"); |
| 1412 | |
| 1413 | std::sort(ctx->part_list.begin(), ctx->part_list.end(), |
| 1414 | [](auto& a, auto& b) { return a.first < b.first; }); |
| 1415 | for (auto& part : ctx->part_list) { |
| 1416 | req_body.appends( |
| 1417 | "<Part>" |
| 1418 | "<PartNumber>", |
| 1419 | part.first, |
| 1420 | "</PartNumber>" |
| 1421 | "<ETag>", |
| 1422 | part.second, |
| 1423 | "</ETag>" |
| 1424 | "</Part>"); |
| 1425 | } |
| 1426 | req_body.appends("</CompleteMultipartUpload>"); |
| 1427 | |
| 1428 | struct iovec iov{(void*)(req_body.data()), req_body.size()}; |
| 1429 | |
| 1430 | iovector_view view(&iov, 1); |
| 1431 | OssUrl oss_url(m_endpoint, m_bucket, ctx->obj_path, m_is_http); |
| 1432 | |
| 1433 | DEFER(delete ctx); |
| 1434 | |
| 1435 | // must appear in dictionary order! |
| 1436 | DEFINE_ORDERED_STRING_KV(query_params, |
| 1437 | {{OSS_PARAM_KEY_UPLOAD_ID, ctx->upload_id}}); |
| 1438 | DEFINE_ONSTACK_OP(m_client, Verb::POST, oss_url.append_params(query_params)); |
| 1439 | op.req.headers.content_length(req_body.size()); |
| 1440 | op.body_writer = {&view, &body_writer_cb}; |
| 1441 | int r = sign_and_call(op, Verb::POST, oss_url, query_params); |
| 1442 | if (r < 0) return r; |
| 1443 | r = verify_crc64_if_needed(op, oss_url.object(), opts.expected_crc64); |
| 1444 | if (r < 0) return r; |
| 1445 | fill_upload_response(op, opts); |
| 1446 | return r; |
| 1447 | } |
| 1448 | |
| 1449 | int OssClient::abort_multipart_upload(void* context) { |
| 1450 | assert(context); |