/// <summary> /// Split a file into chunks /// </summary> /// <param name="initiateFileUploadInfo">The information used to initiate a file upload</param> /// <param name="initiateFileUploadResponse">The response to the request to initiate a file upload</param> /// <returns>Information to upload each chunk</returns> private IList <UploadChunkInfo> ChunkFile(InitiateFileUploadInfo initiateFileUploadInfo, InitiateFileUploadResponse initiateFileUploadResponse) { var chunks = new List <UploadChunkInfo>(); using (var inputFileStream = File.OpenRead(initiateFileUploadInfo.Path)) { for (int chunkIndex = 0; chunkIndex < initiateFileUploadInfo.NumberOfChunks; chunkIndex++) { string chunkPath; long chunkSize; if (initiateFileUploadInfo.NumberOfChunks > 1) { chunkPath = Path.GetTempFileName(); // When chunking, chunk size can NOT be smaller than UPLOAD_CHUNK_SIZE_IN_BYTES, i.e., the last chunk will // be between UPLOAD_CHUNK_SIZE_IN_BYTES and 2 * UPLOAD_CHUNK_SIZE_IN_BYTES chunkSize = chunkIndex < initiateFileUploadInfo.NumberOfChunks - 1 ? UPLOAD_CHUNK_SIZE_IN_BYTES : initiateFileUploadInfo.FileSize - chunkIndex * UPLOAD_CHUNK_SIZE_IN_BYTES; using (var outputFileStream = File.OpenWrite(chunkPath)) { var totalNumberOfBytesCopied = CopyStream(inputFileStream, outputFileStream, chunkSize); if (totalNumberOfBytesCopied != chunkSize) { var message = $"Error creating chunk {chunkIndex + 1} of {initiateFileUploadInfo.NumberOfChunks} for file {initiateFileUploadInfo.Path}. Only {totalNumberOfBytesCopied} of {chunkSize} bytes were copied."; _logger.LogError(message); throw new ProKnowException(message); } } } else { chunkPath = initiateFileUploadInfo.Path; chunkSize = initiateFileUploadInfo.FileSize; } var chunk = new UploadChunkInfo { InitiateFileUploadInfo = initiateFileUploadInfo, InitiateFileUploadResponse = initiateFileUploadResponse, ChunkIndex = chunkIndex, ChunkPath = chunkPath, ChunkSize = chunkSize }; chunks.Add(chunk); } } return(chunks); }
/// <summary> /// Uploads a chunk of a file /// </summary> /// <param name="uploadChunkInfo">The information to upload a chunk</param> private async Task UploadChunkAsync(UploadChunkInfo uploadChunkInfo) { await _semaphore.WaitAsync(); try { var headerKeyValuePairs = new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("Proknow-Key", uploadChunkInfo.InitiateFileUploadResponse.Key) }; using (var content = new MultipartFormDataContent()) { content.Add(new StringContent((uploadChunkInfo.ChunkIndex + 1).ToString()), "flowChunkNumber"); content.Add(new StringContent(UPLOAD_CHUNK_SIZE_IN_BYTES.ToString()), "flowChunkSize"); content.Add(new StringContent(uploadChunkInfo.ChunkSize.ToString()), "flowCurrentChunkSize"); content.Add(new StringContent(uploadChunkInfo.InitiateFileUploadInfo.NumberOfChunks.ToString()), "flowTotalChunks"); content.Add(new StringContent(uploadChunkInfo.InitiateFileUploadInfo.FileSize.ToString()), "flowTotalSize"); content.Add(new StringContent(uploadChunkInfo.InitiateFileUploadResponse.Identifier), "flowIdentifier"); content.Add(new StringContent(uploadChunkInfo.InitiateFileUploadInfo.Path), "flowFilename"); content.Add(new StringContent((uploadChunkInfo.InitiateFileUploadInfo.NumberOfChunks > 1).ToString().ToLower()), "flowMultipart"); using (var fs = File.OpenRead(uploadChunkInfo.ChunkPath)) { content.Add(new StreamContent(fs), "file", uploadChunkInfo.InitiateFileUploadInfo.Path); var response = await _proKnow.Requestor.PostAsync("/uploads/chunks", headerKeyValuePairs, content); _logger.LogDebug($"Uploaded file {uploadChunkInfo.InitiateFileUploadInfo.Path} chunk {uploadChunkInfo.ChunkIndex + 1} of {uploadChunkInfo.InitiateFileUploadInfo.NumberOfChunks}. Response: {response}"); } } } catch (Exception ex) { _logger.LogError($"Error uploading chunk {uploadChunkInfo.ChunkIndex + 1} of {uploadChunkInfo.InitiateFileUploadInfo.NumberOfChunks} for file {uploadChunkInfo.InitiateFileUploadInfo.Path}. {ex}"); throw; } finally { _semaphore.Release(); } }