- 数据工程
- 数据湖
- 大数据
- 对象存储
- 后端
【免费下载链接】lakeFS
lakeFS - Data version control for your data lake | Git for data
导读
UploadPartCopyFrom是 lakeFS Rust SDK 中用于预签名分片上传复制(presigned multipart upload copy)的请求体模型:它描述"如何把另一个对象的某一段字节复制为当前分片上传的一个 part"。本文以 UploadPartCopyFrom.md 为核心,结合 Rust 模型源码、OpenAPI 定义(api/swagger.yml)、服务端控制器实现(pkg/api/controller.go)以及端到端测试(esti/presign_multipart_test.go),完整讲解该模型的字段语义、它与CopyPartSource的嵌套关系、底层upload_part_copy接口的调用链,并给出可直接运行的 Rust 代码示例。
模型概览:UploadPartCopyFrom 是什么
UploadPartCopyFrom对应 HTTP 接口PUT /repositories/{repository}/branches/{branch}/staging/pmpu/{uploadId}/parts/{partNumber}/copy的 JSON 请求体,其作用是在 lakeFS 的预签名分片上传流程中,通过"服务端复制"而非"上传字节"的方式填充一个分片(part)。它由 openapi-generator 根据api/swagger.yml自动生成,仓库中的 Rust 实现位于 src/models/upload_part_copy_from.rs。
原文档给出的字段定义如下:
| Name | Type | Description | Notes |
|---|---|---|---|
| physical_address | String | The physical address (of the entire intended object) returned from createPresignMultipartUpload. | |
| copy_source | models::CopyPartSource |
从 OpenAPI 定义(api/swagger.yml)可以看出,UploadPartCopyFrom并不是一个独立的平铺结构,而是通过allOf继承UploadPartFrom(该父模型只包含physical_address字段),再追加copy_source字段:
UploadPartCopyFrom: allOf: # Extend UploadPartFrom - $ref: "#/components/schemas/UploadPartFrom" - type: object properties: copy_source: description: "Source of copy" $ref: "#/components/schemas/CopyPartSource" required: - copy_source生成的 Rust 结构体与之一一对应(upload_part_copy_from.rs):
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct UploadPartCopyFrom { /// The physical address (of the entire intended object) returned from createPresignMultipartUpload. #[serde(rename = "physical_address")] pub physical_address: String, #[serde(rename = "copy_source")] pub copy_source: Box<models::CopyPartSource>, }字段语义:physical_address
physical_address是目标对象整个分片上传的物理地址,必须取自createPresignMultipartUpload(即POST /repositories/{repository}/branches/{branch}/staging/pmpu)接口的响应PresignMultipartUpload.physical_address(见 swagger.yml)。它把"本次复制要写入哪个上传会话"锚定在分片上传上,而不是每次复制都重新指定目标。
服务端在 pkg/api/controller.go 中会对其做两重校验:
- 通过
normalizePhysicalAddress判断其地址类型必须是相对存储命名空间(AddressTypeRelative)的相对地址,否则返回 400; - 调用
Catalog.VerifyLinkAddress验证该地址已经为链接而保存(即该物理地址属于本次分片上传注册过的地址),防止把任意物理地址写入分片。
嵌套模型:CopyPartSource
copy_source描述"从哪个对象、哪个区间复制字节"。其 Rust 实现位于 src/models/copy_part_source.rs,对应文档 CopyPartSource.md,字段如下:
| Name | Type | Description | Notes |
|---|---|---|---|
| repository | String | 源对象所在仓库 | |
| r#ref | String | 源对象所在引用(分支、tag 或 commit ID) | |
| path | String | 源对象在引用下的路径 | |
| range | Option<String> | 要复制的字节区间 | [optional] |
其中range的可选值是带模式校验的字符串,在 api/swagger.yml 中定义为:
range: description: "Range of bytes to copy" type: string pattern: '^bytes=((\d*-\d*,? ?)+)$'即必须形如bytes=start-end(如bytes=0-4194303)。注意该区间是闭区间(inclusive)——测试 esti/presign_multipart_test.go 中的注释明确指出 "the Range header isinclusive. So this drops the last byte",也就是说想复制第 1 到第 N-1 字节需写bytes=1-(N-2)。
由于r#ref中的ref是 Rust 关键字,生成的字段名为r#ref,使用原始标识符语法访问(copy_source.r#ref)。而CopyPartSource::new构造函数只要求repository、ref、path三个必填参数,range通过Option留空(copy_part_source.rs):
pub fn new(repository: String, r#ref: String, path: String) -> CopyPartSource { CopyPartSource { repository, r#ref, path, range: None, } }服务端复制语义:range 有无决定了两种底层调用
服务端处理uploadPartCopy时(controller.go)会根据copy_source.range是否设置,分派到块存储适配器的两个不同接口:
- 未设置 range:调用
c.BlockAdapter.UploadCopyPart(ctx, srcObjectRef, dstObjectRef, uploadID, partNumber),即把源对象的整个内容作为一个 part 复制; - 设置 range:用
fmt.Sscanf(*rng, "bytes=%d-%d", &startPosition, &endPosition)解析起止位置后,调用c.BlockAdapter.UploadCopyPartRange(ctx, srcObjectRef, dstObjectRef, uploadID, partNumber, startPosition, endPosition),实现"只复制源对象的一段字节"。
这里的srcObjectRef通过Catalog.GetEntry先从源仓库/引用/路径解析出源 entry,再取其PhysicalAddress构造(controller.go)。复制成功后接口返回204 No Content,并在响应头ETag中给出该 part 的 ETag——这个 ETag 正是后续completePresignMultipartUpload组装分片时需要提交的内容(见 swagger.yml 中 "ETag of the uploaded part, pass in completion")。
此外,该接口的鉴权要求是同时满足两个权限节点(NodeTypeAnd,见 controller.go):对目标路径的WriteObjectAction,以及对源路径的ReadObjectAction。这意味着复制操作天然需要"写目标 + 读源"的双重授权。
前置流程:presign 分片上传的完整生命周期
UploadPartCopyFrom只是预签名分片上传链路中的一环。结合 ExperimentalApi.md 与 swagger.yml,完整的生命周期为:
- createPresignMultipartUpload(
POST /repositories/{repository}/branches/{branch}/staging/pmpu):初始化分片上传,返回PresignMultipartUpload——包含upload_id、physical_address以及可选的presigned_urls数组(见 PresignMultipartUpload.md)。part 编号从 1 开始,除最后一个 part 外,每个 part 有最小大小限制,取决于底层块存储实现(例如 S3 块存储下为 5MB)。 - upload_part / upload_part_copy(
PUT .../parts/{partNumber}与PUT .../parts/{partNumber}/copy):逐个填充 part。前者返回一个用于直传的预签名 URL,后者通过UploadPartCopyFrom实现服务端复制。注意part_number的取值范围为 1 到 10000(见 swagger.yml)。 - completePresignMultipartUpload(
PUT .../pmpu/{uploadId}):提交CompletePresignMultipartUpload(包含按升序排列的parts列表、physical_address、可选的user_metadata与content_type),服务端把各个 part 组装成最终对象(见 swagger.yml)。
Rust SDK 中对应的异步函数签名位于 src/apis/experimental_api.rs:
pub async fn upload_part_copy( configuration: &configuration::Configuration, repository: &str, branch: &str, upload_id: &str, path: &str, part_number: i32, upload_part_copy_from: models::UploadPartCopyFrom, ) -> Result<(), Error<UploadPartCopyError>>其构造的请求 URL 为{base_path}/repositories/{repository}/branches/{branch}/staging/pmpu/{uploadId}/parts/{partNumber}/copy,请求体以 JSON 序列化UploadPartCopyFrom发送,成功时返回空响应体(204 No Content)。
Rust 实战示例:复制源对象的一段字节作为 part
下面这段示例演示如何在 Rust SDK 中构造UploadPartCopyFrom,把main分支下large_data.bin的第 1 至第 N-2 字节复制为分片上传的第 1 个 part(边界取自仓库端到端测试 esti/presign_multipart_test.go 的复制用例):
use lakefs_sdk::models::{CopyPartSource, UploadPartCopyFrom}; use lakefs_sdk::apis::{configuration::Configuration, experimental_api}; let config = Configuration::new(); // 1. 初始化预签名分片上传 let resp_create = experimental_api::create_presign_multipart_upload( &config, "my-repo", "main", "dest/data.bin", Some(2), ).await?; let upload_id = resp_create.upload_id; let physical_address = resp_create.physical_address; // 2. 构造复制请求体:复制源对象的一个字节区间(闭区间) let copy_from = UploadPartCopyFrom { physical_address: physical_address.clone(), copy_source: Box::new(CopyPartSource { repository: "my-repo".to_string(), r#ref: "main".to_string(), path: "large_data.bin".to_string(), range: Some(format!("bytes=1-{}", large_data_content_length - 2)), }), }; // 3. 执行复制:part 编号从 1 开始 experimental_api::upload_part_copy( &config, "my-repo", "main", &upload_id, "dest/data.bin", 1, copy_from, ).await?;之后继续用upload_part填充剩余 part(最后一个 part 需由客户端实际上传),再调用complete_presign_multipart_upload提交parts(含每个 part 的 ETag)完成组装,最终对象即可通过stat_object校验其 checksum 与总大小(参见 presign_multipart_test.go 的验证逻辑)。
使用场景与注意事项
典型场景:当目标对象的大部分数据与仓库内已有对象重合时(例如"对已有大文件做小范围修改后形成新对象"),可以只把修改产生的少量 part 用预签名 URL 直传,其余 part 全部通过UploadPartCopyFrom走服务端复制,从而大幅减少客户端到对象存储之间的数据传输。
注意事项:
physical_address必须来自createPresignMultipartUpload的响应,且必须是相对存储命名空间的地址,否则服务端返回 400;copy_source的repository/ref/path为必填,range可选;提供range时格式必须匹配^bytes=((\d*-\d*,? ?)+)$且区间为闭区间;- 复制操作需要同时对目标路径具备写权限、对源路径具备读权限;
- 该接口位于
experimental标签下(swagger.yml),属于实验性 API,接口形态可能随版本演进变化;使用前建议以当前仓库的 api/swagger.yml 为准; - 端到端测试 esti/presign_multipart_test.go 仅在块存储类型为 S3 时运行(
skipPresignMultipart判断ViperBlockstoreType != "s3"即跳过),说明该能力与底层块存储的 multipart copy 支持密切相关。
参考路径汇总
- 模型文档:clients/rust/docs/UploadPartCopyFrom.md、clients/rust/docs/CopyPartSource.md、clients/rust/docs/PresignMultipartUpload.md
- Rust 模型源码:clients/rust/src/models/upload_part_copy_from.rs、clients/rust/src/models/copy_part_source.rs
- API 函数实现:clients/rust/src/apis/experimental_api.rs、clients/rust/docs/ExperimentalApi.md
- OpenAPI 定义:api/swagger.yml(模型)、api/swagger.yml(接口)
- 服务端实现:pkg/api/controller.go
- 端到端测试:esti/presign_multipart_test.go
- 数据工程
- 数据湖
- 大数据
- 对象存储
- 后端
【免费下载链接】lakeFS
lakeFS - Data version control for your data lake | Git for data
相关推荐
lakeFS Python SDK 的 UploadPartFrom 模型:预签名分片上传(Presigned Multipart Upload)的逐片上传请求体详解
lakeFS Python SDK 的 UploadPartFrom 模型:预签名分片上传(Presigned Multipart Upload)的逐片上传请求
数据工程数据湖大数据对象存储后端lakeFS Java SDK 的 UploadPartCopyFrom 模型:基于预签名分片上传的跨对象分片复制
lakeFS Java SDK 的 UploadPartCopyFrom 模型:基于预签名分片上传的跨对象分片复制 导读 本文深入讲解 lakeFS Java
数据工程数据湖大数据对象存储后端lakeFS Rust SDK 的 AbortPresignMultipartUpload:预签名分片上传中止请求模型完全解析
lakeFS Rust SDK 的 AbortPresignMultipartUpload:预签名分片上传中止请求模型完全解析 lakeFS 为数据湖提供 Gi
数据工程数据湖大数据对象存储后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考