private static PrepareWriteResult ParsePrepareResult(INativeData data) { PrepareWriteResult res = new PrepareWriteResult(); unsafe { byte * dataPtr = (byte *)data.DataPtr.ToPointer(); ulong *raftGroupIdPtr = (ulong *)dataPtr; res.ChunkRaftGroupId = *raftGroupIdPtr; Guid *pathIdPtr = (Guid *)(dataPtr + 8); res.ParentPathId = *pathIdPtr; } return(res); }
public static async ValueTask UploadAsync(byte appId, Stream stream, string toPath) { //TODO:判断长度超过限制 if (!toPath.StartsWith('/')) { throw new ArgumentException("Path must start with '/'", nameof(toPath)); } if (string.IsNullOrEmpty(Path.GetFileName(toPath))) { throw new ArgumentException("Path must has file name", nameof(toPath)); } var needSize = (uint)stream.Length; //1. 先生成写事务标识 Guid txnId = GenWriteTxnId(); //2. 提议至BlobMetaRaftGroup准备写 var res = await PrepareWriteAsync(appId, txnId, toPath, needSize); if (res == null) { throw new Exception("准备写结果为空"); } PrepareWriteResult result = ParsePrepareResult(res); res.Dispose(); if (result.ChunkRaftGroupId == 0) //上级目录没有可用的Chunk,则提议创建新的 { res = await TryCreateChunkAsync(appId, Path.GetDirectoryName(toPath), needSize); result = ParsePrepareResult(res); res.Dispose(); } //Log.Debug($"ChunkRaftGroupId = {result.ChunkRaftGroupId} 8864812498945"); //3. 循环写入块,最后一块带结束标记(引擎计算新旧文件大小差异) //TODO:优化内存复制 string fileName = Path.GetFileName(toPath); byte[] fileNameData = System.Text.Encoding.UTF8.GetBytes(fileName); int pathSize = 32 + fileNameData.Length; IntPtr pathPtr = Marshal.AllocHGlobal(pathSize); unsafe { var idPtr = (Guid *)pathPtr.ToPointer(); idPtr[0] = result.ParentPathId; idPtr[1] = txnId; var namePtr = pathPtr + 32; Marshal.Copy(fileNameData, 0, namePtr, fileNameData.Length); } var buffer = new byte[SegmentSize]; int bytesRead = 0; int totalRead = 0; uint option = 0; IntPtr nativeDataPtr; do { bytesRead = stream.Read(buffer); if (bytesRead <= 0) { break; } totalRead += bytesRead; if (totalRead >= stream.Length) { option = 2; } unsafe { nativeDataPtr = NativeApi.NewNativeString(bytesRead, out byte *dataPtr); Marshal.Copy(buffer, 0, new IntPtr(dataPtr), bytesRead); } await StoreApi.Api.BlobWriteChunkAsync(result.ChunkRaftGroupId, pathPtr, (uint)pathSize, option, nativeDataPtr); if (option == 2) { break; } option = 1; } while (true); //释放分配的pathPtr buffer = null; Marshal.FreeHGlobal(pathPtr); }