using System; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class FileChunkUploader { private readonly HttpClient _httpClient; private const int MaxRetries = 3; private const int InitialDelayMs = 2000; // 2 seconds public FileChunkUploader(HttpClient httpClient) { _httpClient = httpClient; } public async Task UploadFileInChunksAsync(string filePath, string endpoint, string username, string apiKey, UploadProgress progress = null) { // Append username and timestamp as query parameters. string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string endpointWithParams = $"{endpoint}?username={Uri.EscapeDataString(username)}×tamp={timestamp}"; // Configure HttpClient headers. _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("x-api-key", apiKey); _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); const int chunkSize = 3 * 1024 * 1024; // 3 MB chunks; adjust as needed. byte[] buffer = new byte[chunkSize]; FileInfo fileInfo = new FileInfo(filePath); long fileSize = fileInfo.Length; int totalChunks = (int)Math.Ceiling((double)fileSize / chunkSize); int chunkIndex = 0; string s3_uri = null; using (var fileStream = File.OpenRead(filePath)) { int bytesRead; while ((bytesRead = await fileStream.ReadAsync(buffer, 0, chunkSize)) > 0) { chunkIndex++; if (progress != null) { progress.Current = (float)chunkIndex / totalChunks; } // For the last chunk, copy only the exact number of bytes. byte[] chunkData = (bytesRead == chunkSize) ? buffer : buffer[..bytesRead]; // Convert the chunk to Base64. string chunkBase64 = Convert.ToBase64String(chunkData); // Build JSON payload including metadata. var payload = new { file_name = fileInfo.Name, chunk_index = chunkIndex, total_chunks = totalChunks, chunk_base64 = chunkBase64 }; string jsonPayload = JsonSerializer.Serialize(payload); using (var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json")) { bool uploaded = false; int retries = 0; int delay = InitialDelayMs; while (!uploaded && retries < MaxRetries) { HttpResponseMessage response = await _httpClient.PostAsync(endpointWithParams, content); if (response.IsSuccessStatusCode) { Console.WriteLine($"Chunk {chunkIndex} of {totalChunks} uploaded successfully."); // If this is the final chunk, extract the s3_uri from the response if (chunkIndex == totalChunks) { string responseContent = await response.Content.ReadAsStringAsync(); try { using (JsonDocument doc = JsonDocument.Parse(responseContent)) { if (doc.RootElement.TryGetProperty("s3Address", out JsonElement s3Element)) { s3_uri = s3Element.GetString(); Console.WriteLine($"File uploaded to S3: {s3_uri}"); } } } catch (JsonException ex) { Console.WriteLine($"Error parsing response JSON: {ex.Message}"); } } uploaded = true; } else { // Log the response body if available for more details. string errorDetails = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Chunk {chunkIndex} upload failed: {response.StatusCode} - {errorDetails}"); retries++; if (retries < MaxRetries) { Console.WriteLine($"Retrying chunk {chunkIndex} in {delay}ms..."); await Task.Delay(delay); delay *= 2; // Exponential backoff. } else { throw new Exception($"Chunk upload failed for chunk {chunkIndex} after {MaxRetries} attempts"); } } } } } } return s3_uri; } }