This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 0c1cdb59c fix(services/gcs): migrate to new multipart impl for
gcs_insert_object_request (#2838)
0c1cdb59c is described below
commit 0c1cdb59c70cada10a448501aeb15b1b639c9c60
Author: Mingzhuo Yin <[email protected]>
AuthorDate: Fri Aug 25 10:40:08 2023 +0800
fix(services/gcs): migrate to new multipart impl for
gcs_insert_object_request (#2838)
* fix(services/gcs): migrate to new multipart impl for
gcs_insert_object_request
Signed-off-by: silver-ymz <[email protected]>
* add new ci to test gcs with default storage class
Signed-off-by: silver-ymz <[email protected]>
* add support for stream
Signed-off-by: silver-ymz <[email protected]>
* use json!() to build metadata
Signed-off-by: silver-ymz <[email protected]>
---------
Signed-off-by: silver-ymz <[email protected]>
---
.github/workflows/service_test_gcs.yml | 20 +++++++++++++++
core/src/services/gcs/core.rs | 46 +++++++++++++++++++++-------------
2 files changed, 48 insertions(+), 18 deletions(-)
diff --git a/.github/workflows/service_test_gcs.yml
b/.github/workflows/service_test_gcs.yml
index 53c324644..ca3bde624 100644
--- a/.github/workflows/service_test_gcs.yml
+++ b/.github/workflows/service_test_gcs.yml
@@ -55,3 +55,23 @@ jobs:
OPENDAL_GCS_ROOT: ${{ secrets.OPENDAL_GCS_ROOT }}
OPENDAL_GCS_BUCKET: ${{ secrets.OPENDAL_GCS_BUCKET }}
OPENDAL_GCS_CREDENTIAL: ${{ secrets.OPENDAL_GCS_CREDENTIAL }}
+
+ gcs-with-default-storage-class:
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push' ||
!github.event.pull_request.head.repo.fork
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Rust toolchain
+ uses: ./.github/actions/setup
+ with:
+ need-nextest: true
+ - name: Test
+ shell: bash
+ working-directory: core
+ run: cargo nextest run gcs
+ env:
+ OPENDAL_GCS_TEST: ${{ secrets.OPENDAL_GCS_TEST }}
+ OPENDAL_GCS_ROOT: ${{ secrets.OPENDAL_GCS_ROOT }}
+ OPENDAL_GCS_BUCKET: ${{ secrets.OPENDAL_GCS_BUCKET }}
+ OPENDAL_GCS_CREDENTIAL: ${{ secrets.OPENDAL_GCS_CREDENTIAL }}
+ OPENDAL_GCS_DEFAULT_STORAGE_CLASS: STANDARD
diff --git a/core/src/services/gcs/core.rs b/core/src/services/gcs/core.rs
index 35cf401ee..3608b445c 100644
--- a/core/src/services/gcs/core.rs
+++ b/core/src/services/gcs/core.rs
@@ -23,7 +23,6 @@ use std::time::Duration;
use backon::ExponentialBuilder;
use backon::Retryable;
use bytes::Bytes;
-use bytes::BytesMut;
use http::header::CONTENT_LENGTH;
use http::header::CONTENT_RANGE;
use http::header::CONTENT_TYPE;
@@ -37,6 +36,7 @@ use reqsign::GoogleCredentialLoader;
use reqsign::GoogleSigner;
use reqsign::GoogleToken;
use reqsign::GoogleTokenLoader;
+use serde_json::json;
use super::uri::percent_encode_path;
use crate::raw::*;
@@ -234,28 +234,38 @@ impl GcsCore {
req = req.header(CONTENT_LENGTH, size.unwrap_or_default());
if let Some(storage_class) = &self.default_storage_class {
- req = req.header(CONTENT_TYPE, "multipart/related;
boundary=my-boundary");
+ let mut multipart = Multipart::new();
- let mut req_body = BytesMut::with_capacity(100);
- write!(
- &mut req_body,
- "--my-boundary\nContent-Type: application/json;
charset=UTF-8\n\n{{\"storageClass\": \"{}\"}}\n\n--my-boundary\n",
- storage_class
- ).unwrap();
+ multipart = multipart.part(
+ FormDataPart::new("metadata")
+ .header(
+ CONTENT_TYPE,
+ "application/json; charset=UTF-8".parse().unwrap(),
+ )
+ .content(json!({"storageClass":
storage_class}).to_string()),
+ );
- if let Some(mime) = content_type {
- write!(&mut req_body, "Content-Type: {}\n\n", mime).unwrap();
- } else {
- write!(&mut req_body, "Content-Type:
application/octet-stream\n\n").unwrap();
- }
+ let mut media_part = FormDataPart::new("media").header(
+ CONTENT_TYPE,
+ content_type
+ .unwrap_or("application/octet-stream")
+ .parse()
+ .unwrap(),
+ );
- if let AsyncBody::Bytes(bytes) = body {
- req_body.extend_from_slice(&bytes);
+ match body {
+ AsyncBody::Empty => {}
+ AsyncBody::Bytes(bytes) => {
+ media_part = media_part.content(bytes);
+ }
+ AsyncBody::Stream(stream) => {
+ media_part = media_part.stream(size.unwrap(), stream);
+ }
}
- write!(&mut req_body, "\n--my-boundary").unwrap();
- let req_body = AsyncBody::Bytes(req_body.freeze());
- let req = req.body(req_body).map_err(new_request_build_error)?;
+ multipart = multipart.part(media_part);
+
+ let req = multipart.apply(Request::post(url))?;
Ok(req)
} else {
if let Some(content_type) = content_type {