本文檔描述了上傳檔案到 PDS 的最佳實務,您可以參考該文檔實現上傳檔案功能。
PDS 檔案類型
檔案夾:目錄類型的檔案,自身不承載任何物理資料,所以直接調用 PDS 建立檔案介面即可。
檔案:非目錄類型的檔案,自身包含真實的檔案資料,建立此類型檔案涉及到檔案上傳步驟,下面文檔主要針對此類型的檔案進行上傳流程介紹。
檔案上傳流程
流程簡述
PDS 的檔案上傳分為以下三步:
調用 PDS CreateFile - 建立檔案或檔案夾介面初始設定檔案,PDS 會返迴文件的元資訊以及 HTTP 上傳地址。
上傳檔案到第一步返回的 HTTP 上傳地址中。
調用 PDS CompleteFile - 完成檔案上傳介面,完成上傳流程。

詳細流程
下面以上傳本地檔案做一個簡單的樣本。
1.建立檔案
簡述:調用建立檔案介面,初始設定檔案上傳。
核心參數設定:
設定上傳到哪個空間下:drive_id = "testDriveId";
設定上傳到哪個目錄下:parent_file_id = "root",root 表示上傳到根目錄下;
設定檔案名稱:name = "testName.jpg";
設定檔案類型:type = "file",file 表示檔案,folder 表示目錄;
設定檔案大小(本地檔案真實大小,單位為位元組):size = 13381200;
設定分區列表:part_info_list = [{"part_number":1},{"part_number":2},{"part_number":3}],這裡要根據檔案大小和用戶端定義的分區大小來計算。設定多個分區可以提高上傳成功率,還可以進行斷點續傳。樣本裡的檔案大小約 13MB,用戶端可以將分區大小設定為 5MB,因此計算得出需要上傳三個分區。
請求 body 樣本:
{
"drive_id":"testDriveId",
"name":"testName.jpg",
"parent_file_id":"root",
"part_info_list":[
{
"part_number":1
},
{
"part_number":2
},
{
"part_number":3
}
],
"size":13381200,
"type":"file"
}返回 body 樣本:
{
"parent_file_id":"root",
"part_info_list":[
{
"part_number":1,
"upload_url":"https://xxxx1"
},
{
"part_number":2,
"upload_url":"https://xxxx2"
},
{
"part_number":3,
"upload_url":"https://xxxx3"
}
],
"upload_id":"testUploadId",
"rapid_upload":false,
"type":"file",
"file_id":"testFileId",
"revision_id":"testRevisionId",
"domain_id":"testDomainId",
"drive_id":"testDriveId",
"file_name":"testName.jpg"
}核心返回參數解析:
file_id: 雲端給檔案分配的唯一 id,在同一 drive 空間內,每個檔案都有唯一的 file_id。
upload_id: 雲端給本次上傳過程分配的 id,後續 complete 等流程都需要和該 upload_id 關聯。
part_info_list:雲端給本次上傳過程分配的分區上傳地址清單,對應上行參數中的 part_info_list,雲端會給每個分區分配一個上傳地址。
2.上傳檔案
簡述:根據第一步返回的分區上傳地址,遍曆上傳每個分區,HTTP Method 為 PUT。
下面以 Java 程式碼範例:
// 遍曆所有分區
for (PartInfo uploadPartInfo : partInfoList) {
// 計算分區在本地檔案中的位置
int number = uploadPartInfo.getPartNumber();
long pos = (number - 1) * partSize;
long size = Math.min(length - pos, partSize);
byte[] partContent = new byte[(int) size];
// 從本地檔案中讀取分區內容到記憶體中
RandomAccessFile randomAccessFile = new RandomAccessFile(localFile, "r");
randomAccessFile.seek(pos);
randomAccessFile.readFully(partContent, 0, (int) size);
randomAccessFile.close();
// 上傳分區
RequestBody body = RequestBody.create(null, partContent);
Request request = new Request.Builder()
.url(uploadPartInfo.getUploadUrl())
.header("Content-Length", String.valueOf(size))
.put(body)
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Response response = okHttpClient.newCall(request).execute();
// 判斷分區是否上傳成功
if (!response.isSuccessful()) {
System.out.println(response.body().string() + "\n");
Assert.fail("upload part failed, partNumber:" + number);
return "";
}
System.out.println("upload part success, partNumber:" + number);
}3.完成檔案上傳
簡述:當第二步中所有的分區都上傳完成後,調用完成檔案上傳介面,完成上傳流程。
核心參數設定:
設定檔案所在的空間 id:drive_id = "testDriveId"。
設定檔案 id:file_id = "testFileId",file_id 填寫為第一步建立檔案返回的 file_id。
設定上傳流程 id:upload_id = "testUploadId",upload_id 填寫為第一步建立檔案介面返回的 upload_id。
請求 body 樣本:
{
"drive_id":"testDriveId",
"file_id":"testFileId",
"upload_id":"testUploadId"
}介面返回 HTTP 狀態代碼 200 後,表示檔案已經上傳完成。
返回 body 樣本:
{
"domain_id":"testDomainId",
"drive_id":"testDriveId",
"file_id":"testFileId",
"parent_file_id":"root",
"type":"file",
"file_extension":"jpg",
"name":"testName.jpg",
"size":13381200,
"status":"available",
"content_hash":"xxxxx",
"created_at":"2023-01-16T11:55:12.166Z",
"updated_at":"2023-01-16T11:55:13.368Z"
}斷點續傳
應用情境
使用者在上傳檔案過程中,可以臨時暫停上傳檔案,比如使用者只希望在 WIFI 情境下上傳檔案,則可以在切換到非 WIFI 情境時暫停上傳,等切換回 WIFI 後可以從中斷點繼續上傳檔案。
實現方式
前述提到用戶端可以在上傳檔案時,對檔案進行分區上傳。
以下面上傳為例:上傳 50MB 的檔案,假設用戶端按照 5 MB 進行分區,則一共可以分成 10 個分區。
![]()
當用戶端已經上傳成功前 6 個分區,第 7 片已經上傳了一段資料時,使用者點擊了暫停上傳。此時用戶端需要將上傳的中間資訊(檔案資訊、上傳進度)持久化下來,比如可以儲存到資料庫中。
![]()
當使用者再次點擊上傳時,第 7 片雖然已經上傳過一段資料,但是該分區並沒有上傳完整,會被作廢,所以從第 7 片開始需要重新上傳完整的分區。此時後面分區的上傳 URL 可能已經到期,上傳介面會返回 403,此時可以調用ListUploadedParts - 列舉已上傳分區,重新擷取已到期分區的上傳地址。
因為檔案存在上傳時限,如果檔案暫停上傳超過 10 天后,則無法再繼續斷點續傳該檔案,此時用戶端只能重新調用建立檔案介面,建立新的雲端檔案並進行上傳。
檔案秒傳能力
秒傳介紹
PDS 提供了 Domain 級檔案去重的能力,使用者上傳檔案時,如果檔案在雲端的該 Domain 下已經存在,無需再走完整的上傳流程,只需要本地計算出檔案的 SHA1,即可進行秒傳。
應用情境
使用者 A 上傳了一部影片,使用者 B 後續再次上傳相同影片時,只需走秒傳,即可在使用者 B 的空間內組建檔案,B 不需要再完整上傳該影片,既提高了上傳效率,又節省了上傳流量。
使用者 B 秒傳上去的檔案和使用者 A 空間下的檔案在 File 層級沒有任何關聯,無需擔心資料安全問題。
使用秒傳
秒傳需要提前計算出檔案完整的 SHA1,在調用建立檔案介面時,設定到參數 content_hash 中。
核心參數設定:
設定檔案秒傳計算演算法,目前只支援 SHA1,content_hash_name = "sha1";
設定檔案 sha1:content_hash = "xxxx";
設定檔案 size:size = 13381200;
其他參數和前述檔案上傳流程章節的建立檔案參數一致。
請求 body 樣本:
{
"drive_id":"testDriveId",
"name":"testName.jpg",
"parent_file_id":"root",
"content_hash":"xxxxx",
"content_hash_name":"sha1",
"part_info_list":[
{
"part_number":1
},
{
"part_number":2
},
{
"part_number":3
}
],
"size":13381200,
"type":"file"
}sha1 計算範例程式碼(Java):
public static String getFileHash(File file) throws IOException {
return Hex.encodeHexString((getFileHashBytes(file)));
}
public static byte[] getFileHashBytes(File file) throws IOException {
byte[] sha1;
try {
MessageDigest digest = MessageDigest.getInstance("SHA1");
byte[] buffer = new byte[10 * 1024];
FileInputStream is = new FileInputStream(file);
int len;
while ((len = is.read(buffer)) != -1) {
digest.update(buffer, 0, len);
}
is.close();
sha1 = digest.digest();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA1 algorithm not found.");
}
return sha1;
}返回 body 樣本:
{
"domain_id":"testDomainId",
"drive_id":"testDriveId",
"parent_file_id":"root",
"upload_id":"testUploadId",
"rapid_upload":true,
"type":"file",
"file_id":"testFileId",
"revision_id":"testRevisionId",
"file_name":"testName.jpg"
}核心返回參數解析:
rapid_upload:表示是否命中秒傳:
a. rapid_upload = true,表示命中秒傳,即雲端該 domain 下存在 data 資料一樣的檔案(根據 SHA1 匹配),直接秒傳成功,返回 body 中也不再返回分區上傳地址,用戶端無需再走後續的【上傳檔案】和【完成檔案上傳】這兩步。
b. rapid_upload = false,表示沒有命中秒傳, 此時返回 body 裡會包含分區的上傳地址,需要再繼續後續的【上傳檔案】和【完成檔案上傳】這兩步。
使用預秒傳提升準確率
秒傳需要計算檔案完整的 SHA1,一般用戶端算力有限,上傳大檔案時計算完整 SHA1 比較耗時,而且雲端如果沒有 SHA1 匹配的 data 資料,也不能秒傳成功,浪費了用戶端的算力,增加了上傳耗時。
為瞭解決這個問題,提升秒傳準確率,PDS 提供了預秒傳能力。用戶端只需要先計算出檔案前 1k 資料的 SHA1,調用 PDS 建立檔案介面,設定到參數 pre_hash 中,服務端會校正該 data 是否可能在雲端已經存在。
核心參數設定:
設定檔案前 1k 資料的 SHA1:pre_hash = "xxxxx";
其他參數和前述檔案上傳流程章節的建立檔案參數一致。
請求 body 樣本:
{
"drive_id":"10530",
"name":"testName.jpg",
"parent_file_id":"root",
"part_info_list":[
{
"part_number":1
},
{
"part_number":2
},
{
"part_number":3
}
],
"pre_hash":"xxxx",
"size":13381200,
"type":"file"
}介面返回解析:
如果雲端返回 HTTP 狀態代碼為 409, 則表明預秒傳匹配成功,也就是雲端可能存在相同資料(因為前 1k 碰撞幾率大,所以不能保證一定存在),此時用戶端可以繼續計算檔案完整 SHA1,再次調用建立檔案介面,嘗試秒傳。
如果雲端返回 HTTP 狀態代碼為 201, 則表明預秒傳沒有匹配成功,也就是雲端此刻肯定不存在相同資料,此時介面會同步返回分區上傳地址,用戶端繼續走完整上傳流程即可。
注意點:預秒傳是後台非同步計算的,可能會出現分鐘級延遲,所以剛上傳到雲端的檔案,再次上傳相同檔案時,不一定能命中預秒傳。
整體流程圖

覆蓋上傳
PDS 支援覆蓋上傳某個檔案,只需要在建立檔案介面中,設定 file_id 為要覆蓋的檔案 id 即可,其他流程和普通上傳一致。
請求 body 樣本:
{
"drive_id":"testDriveId",
"file_id":"testFileId",
"name":"testName.jpg",
"parent_file_id":"root",
"part_info_list":[
{
"part_number":1
},
{
"part_number":2
},
{
"part_number":3
}
],
"size":13373603,
"type":"file"
}關鍵問題:
檔案覆蓋寫後,檔案的 file_id 不會發生變化,還可以使用之前的 file_id 進行檔案操作。
多個端並發覆蓋寫同一個檔案時,會使用最後一個成功調用完成檔案上傳介面的版本作為檔案最新版本。
如果想保留檔案覆蓋寫之前的版本,需要先開啟多版本功能,多版本介面可以參考ListRevision - 列舉版本。
常見問題
檔案分區的上傳地址到期了
檔案分區的上傳地址是有時效的,目前有效期間為 1 個小時,如果 1 個小時後再進行上傳(比如使用者暫停後,斷點續傳情境),則上傳介面會返回 403。此時可以調用ListUploadedParts - 列舉已上傳分區,重新擷取已到期分區的上傳地址。
檔案上傳限制
單個檔案分區最大限制 5GB
因服務端使用流式計算SHA1值,單個檔案的分區需要串列上傳,不支援多個分區並行上傳
分區不允許覆蓋
檔案上傳時限
檔案從開始上傳到最後完成上傳,需要在 10 天內完成,超過10 天后本次上傳流程會被作廢,此時用戶端只能重新調用建立檔案介面,建立新的雲端檔案並進行上傳。