sdk/Domain/ResumableContext.cs (372 lines of code) (raw):

/* * Copyright (C) Alibaba Cloud Computing * All rights reserved. * */ using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using Aliyun.OSS.Util; namespace Aliyun.OSS { internal class ResumablePartContext { protected static readonly char TokenSeparator = '_'; public int PartId { get; set; } public long Position { get; set; } public long Length { get; set; } public PartETag PartETag { get; set; } public bool IsCompleted { get; set; } public ulong Crc64 { get; set; } public bool FromString(string value) { if (string.IsNullOrEmpty(value)) { return false; } var tokens = value.Split(TokenSeparator); if (tokens.Length != 7) { return false; } int partId; if (!int.TryParse(tokens[0], out partId) || partId <= 0) { return false; } PartId = partId; long position; if (!long.TryParse(tokens[1], out position) || position < 0) { return false; } Position = position; long length; if (!long.TryParse(tokens[2], out length) || Length < 0) { return false; } Length = length; bool isCompleted; if (!bool.TryParse(tokens[3], out isCompleted)) { return false; } IsCompleted = isCompleted; int partNum; if (string.IsNullOrEmpty(tokens[4]) && string.IsNullOrEmpty(tokens[5])) { PartETag = null; } else if (!(int.TryParse(tokens[4], out partNum) && partNum > 0 && !string.IsNullOrEmpty(tokens[5]))) { return false; } else { PartETag = new PartETag(partNum, tokens[5]); } ulong crc = 0; if (!ulong.TryParse(tokens[6], out crc)) { return false; } Crc64 = crc; return true; } override public string ToString() { string result = PartId.ToString() + TokenSeparator + Position.ToString() + TokenSeparator + Length.ToString() + TokenSeparator + IsCompleted.ToString() + TokenSeparator; if (PartETag != null) { result += PartETag.PartNumber.ToString() + TokenSeparator + PartETag.ETag + TokenSeparator; } else { result += "" + TokenSeparator + "" + TokenSeparator; } result += Crc64.ToString(); return result; } } internal class ResumableContext { protected static readonly char ContextSeparator = ':'; protected static readonly char PartContextSeparator = ','; public string BucketName { get; protected set; } public string Key { get; protected set; } public List<ResumablePartContext> PartContextList { get; set; } public string UploadId { get; set; } public string ContentMd5 { get; set; } public string Crc64 { get; set; } public string CheckpointFile { get { return string.IsNullOrEmpty(CheckpointDir) ? "" : GenerateCheckpointFile(); } } public string CheckpointDir { get; protected set; } public void Clear() { if (!string.IsNullOrEmpty(CheckpointFile) && File.Exists(CheckpointFile)) { File.Delete(CheckpointFile); } } public bool Load() { if (!string.IsNullOrEmpty(CheckpointFile) && File.Exists(CheckpointFile)) { var content = File.ReadAllText(CheckpointFile); return FromString(content); } return false; } public void Dump() { if (!string.IsNullOrEmpty(CheckpointFile)) { string serialize = ToString(); if (!string.IsNullOrEmpty(serialize)) { File.WriteAllText(CheckpointFile, serialize); } } } virtual public bool FromString(string value) { var tokens = value.Split(ContextSeparator); if (tokens.Length != 4) { return false; } UploadId = tokens[0]; ContentMd5 = tokens[1]; Crc64 = tokens[2]; var partStr = tokens[3]; var partTokens = partStr.Split(PartContextSeparator); if (partTokens.Length < 1) { return false; } PartContextList = PartContextList ?? new List<ResumablePartContext>(); for (int i = 0; i < partTokens.Length; i++) { var partContext = new ResumablePartContext(); if (!partContext.FromString(partTokens[i])) { return false; } PartContextList.Add(partContext); } return true; } override public string ToString() { string result = string.Empty; if (string.IsNullOrEmpty(UploadId)) { return string.Empty; } result += UploadId.ToString() + ContextSeparator; if (ContentMd5 == null) { ContentMd5 = string.Empty; } if (Crc64 == null) { Crc64 = string.Empty; } result += ContentMd5 + ContextSeparator + Crc64 + ContextSeparator; if (PartContextList.Count == 0) { return string.Empty; } foreach (var part in PartContextList) { string partStr = part.ToString(); if (string.IsNullOrEmpty(partStr)) { return string.Empty; } result += partStr + PartContextSeparator; } if (result.EndsWith(new string(PartContextSeparator, 1))) { result = result.Substring(0, result.Length - 1); } return result; } public ResumableContext(string bucketName, string key, string checkpointDir) { BucketName = bucketName; Key = key; if (!string.IsNullOrEmpty(checkpointDir)) { CheckpointDir = checkpointDir; } } public long GetUploadedBytes() { long uploadedBytes = 0; foreach (var part in PartContextList) { if (part.IsCompleted) { uploadedBytes += part.Length; } } return uploadedBytes; } virtual protected string GenerateCheckpointFile() { return GetCheckpointFilePath(CheckpointDir, Md5Hex(BucketName) + "_" + Md5Hex(Key)); } protected string GetCheckpointFilePath(string checkpointDir, string checkpointFileName) { if (checkpointDir != null && (checkpointDir.Length > OssUtils.MaxPathLength - OssUtils.MinPathLength)) { throw new ArgumentException("Invalid checkpoint directory {0}", CheckpointDir); } var maxFileNameSize = Math.Min(OssUtils.MaxPathLength - 1, checkpointFileName.Length); if (checkpointDir != null) { maxFileNameSize = Math.Min(OssUtils.MaxPathLength - CheckpointDir.Length - 1, checkpointFileName.Length); } return CheckpointDir + Path.DirectorySeparatorChar + checkpointFileName.Substring(0, maxFileNameSize); } protected string Base64(string str) { var bytes = Encoding.UTF8.GetBytes(str); var base64Str = Convert.ToBase64String(bytes); return base64Str.Replace("+", "_").Replace("/", "-"); } protected string Md5Hex(string str) { var sBuilder = new StringBuilder(); var bytes = Encoding.UTF8.GetBytes(str); using (var md5 = System.Security.Cryptography.MD5.Create()) { var data = md5.ComputeHash(new MemoryStream(bytes)); foreach (var t in data) { sBuilder.Append(t.ToString("x2")); } } return sBuilder.ToString().ToUpperInvariant(); } } internal class ResumableCopyContext : ResumableContext { public string SourceBucketName { get; private set; } public string SourceKey { get; private set; } public string SourceVersionId { get; private set; } override protected string GenerateCheckpointFile() { var srcPath = SourceBucketName + "-" + SourceKey; if (!string.IsNullOrEmpty(SourceVersionId)) { srcPath = srcPath + "?versionId=" + SourceVersionId; } var dstPath = BucketName + "-" + Key; return GetCheckpointFilePath(CheckpointDir, "Copy_" + Md5Hex(srcPath) + "_" + Md5Hex(dstPath)); } public ResumableCopyContext (string sourceBucketName, string sourceKey, string sourceVersionId, string destBucketName, string destKey, string checkpointDir) : base(destBucketName, destKey, checkpointDir) { SourceBucketName = sourceBucketName; SourceKey = sourceKey; SourceVersionId = sourceVersionId; } } internal class ResumableDownloadContext : ResumableContext { public string VersionId { get; private set; } public ResumableDownloadContext(string bucketName, string key, string versionId, string checkpointDir) :base(bucketName, key, checkpointDir) { VersionId = versionId; } override protected string GenerateCheckpointFile() { var key = string.IsNullOrEmpty(VersionId) ? Key : Key + "?versionId=" + VersionId; return GetCheckpointFilePath(CheckpointDir, "Download_" + Md5Hex(BucketName) + "_" + Md5Hex(key)); } public long GetDownloadedBytes() { return GetUploadedBytes(); } public long GetTotalBytes() { long totalBytes = 0; foreach (var part in PartContextList) { totalBytes += part.Length; } return totalBytes; } public string ETag { get; set; } public override bool FromString(string value) { var tokens = value.Split(ContextSeparator); if (tokens.Length != 4) { return false; } ETag = tokens[0]; if (!string.IsNullOrEmpty(tokens[1])) { ContentMd5 = tokens[1]; } if (!string.IsNullOrEmpty(tokens[2])) { Crc64 = tokens[2]; } var partStr = tokens[3]; var partTokens = partStr.Split(PartContextSeparator); if (partTokens.Length <= 1) { return false; } PartContextList = PartContextList ?? new List<ResumablePartContext>(); for (int i = 0; i < partTokens.Length; i++) { var partContext = new ResumablePartContext(); if (!partContext.FromString(partTokens[i])) { return false; } PartContextList.Add(partContext); } return true; } public override string ToString() { StringBuilder result = new StringBuilder(); result.Append(ETag); result.Append(ContextSeparator); result.Append(ContentMd5); result.Append(ContextSeparator); result.Append(Crc64); result.Append(ContextSeparator); foreach (var part in PartContextList) { string partStr = part.ToString(); result.Append(partStr); result.Append(PartContextSeparator); } string ss = result.ToString(); if (ss.EndsWith(new string(PartContextSeparator, 1))) { ss = ss.Substring(0, ss.Length - 1); } return ss; } } }