This guide explains how to upload bulletins using the Multipart API in a structured way. It involves three main steps:
Initialize the upload using the API.
Upload each part and keep track of the parts.
Call the
Complete
API and pass the part IDs.
Step 1: Initialize the Multipart Upload
The first step is to initialize the upload process by calling the InitializeMultipartUpload
endpoint.
Endpoint:
POST /v1/bulletins/multipart
Request Parameters:
tt
: Represents the type of content being uploaded. For bulletins, use the valuebulletins
.Body:
{ "Name": "BulletinFile.png", "Parts": 5, "ZoneID": 1 }
Expected Response:
{ "UploadId": "unique-upload-id", "Key": "Temp/unique-media-id/BulletinFile.png", "Name": "BulletinFile.png", "Urls": [ "signed-url-part-1", "signed-url-part-2", "signed-url-part-3", "signed-url-part-4", "signed-url-part-5" ] }
Explanation:
The
UploadId
is a unique identifier for this upload session.The
Urls
array contains pre-signed URLs for each part of the file. These URLs allow you to upload the file parts directly t
Step 2: Upload Each Part
Once the upload is initialized, you need to upload each part of the file to the corresponding signed URL.
Procedure:
Split your file into parts (if necessary) according to the
Parts
value specified in the initialization request.Use an HTTP client (like
cURL
, Postman, or any programming language) to upload each part to the respective signed URL.
Example Using cURL:
curl -X PUT "signed-url-part-1" \ -T "part1-of-file" curl -X PUT "signed-url-part-2" \ -T "part2-of-file" # Repeat for all parts
Important Notes:
Ensure that parts are uploaded in the correct order.
After each upload, the response from the server will contain an
ETag
. Save theseETags
as they will be needed in the final step.
Step 3: Complete the Multipart Upload
Once all parts have been uploaded, call the CompleteMultipartUpload
endpoint to finalize the process and register the bulletin in the database.
Endpoint:
POST /v1/bulletins/multipart/complete
Request Parameters:
Body:
{ "UploadId": "unique-upload-id", "Key": "Temp/unique-media-id/BulletinFile.png", "ETags": [ "etag-for-part-1", "etag-for-part-2", "etag-for-part-3", "etag-for-part-4", "etag-for-part-5" ], "ZoneID": 1, "Name": "BulletinFile.png" }
Expected Response:
{ "Id": "bulletin-id", "Name": "BulletinFile.png", "Status": "Uploaded", "ZoneID": 1, "Url": "final-url-to-access-the-file" }
Demonstration with C# Console App
To test this API locally, you can use the following C# console application. This app demonstrates the entire process: initializing the upload, uploading parts, and completing the multipart upload.
C# Code Example
using System.Net.Http.Headers; using System.Text; using System.Text.Json; class Program { private static readonly string ApiBaseUrl = "http://localhost/CarouselAPI/v1"; private static readonly string AuthToken = "<your-token-here>"; // Replace with a valid token. private static readonly int zoneId = 43; // Replace with a valid zone id. static async Task Main() { // Generate test files for upload GenerateTestFiles(3); // Step 1: Initialize Multipart Upload Console.WriteLine("Initializing multipart upload..."); var initializeResponse = await InitializeMultipartUploadAsync("BulletinFile.png", 3); if (initializeResponse == null) { Console.WriteLine("Failed to initialize upload."); return; } Console.WriteLine($"UploadId: {initializeResponse.UploadId}"); Console.WriteLine("Uploading parts..."); // Step 2: Upload Each Part var etags = new List<string>(); for (int i = 0; i < initializeResponse.Urls.Count; i++) { var partUrl = initializeResponse.Urls[i]; var filePath = $"part{i + 1}.bin"; // Replace with actual file paths for testing. var etag = await UploadPartAsync(partUrl, filePath); if (!string.IsNullOrEmpty(etag)) { etags.Add(etag); Console.WriteLine($"Uploaded part {i + 1}: ETag = {etag}"); } else { Console.WriteLine($"Failed to upload part {i + 1}."); return; } } // Step 3: Complete Multipart Upload Console.WriteLine("Completing multipart upload..."); var completeResult = await CompleteMultipartUploadAsync( initializeResponse.UploadId, initializeResponse.Key, initializeResponse.Name, zoneId, etags ); if (completeResult) Console.WriteLine("Multipart upload completed successfully."); else Console.WriteLine("Failed to complete upload."); } private static async Task<MultipartUploadInitializeResponse?> InitializeMultipartUploadAsync(string fileName, int parts) { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); var requestContent = new StringContent(JsonSerializer.Serialize(new { Name = fileName, Parts = parts, ZoneID = zoneId }), Encoding.UTF8, "application/json"); var response = await client.PostAsync($"{ApiBaseUrl}/bulletins/multipart", requestContent); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<MultipartUploadInitializeResponse>(content); } Console.WriteLine($"Failed to initialize upload. Status Code: {response.StatusCode}"); return null; } private static async Task<string?> UploadPartAsync(string url, string filePath) { using var client = new HttpClient(); using var fileStream = File.OpenRead(filePath); var content = new StreamContent(fileStream); content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); var response = await client.PutAsync(url, content); if (response.IsSuccessStatusCode && response.Headers.ETag != null) { return response.Headers.ETag.Tag; // Return ETag from the response. } Console.WriteLine($"Failed to upload part. Status Code: {response.StatusCode}"); return null; } private static async Task<bool> CompleteMultipartUploadAsync(string uploadId, string key, string name, int zoneId, List<string> etags) { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); var requestContent = new StringContent(JsonSerializer.Serialize(new { UploadId = uploadId, Key = key, Name = name, ZoneID = zoneId, ETags = etags }), Encoding.UTF8, "application/json"); var response = await client.PostAsync($"{ApiBaseUrl}/bulletins/multipart/complete", requestContent); if (response.IsSuccessStatusCode) { Console.WriteLine("Upload completed successfully."); return true; } Console.WriteLine($"Failed to complete upload. Status Code: {response.StatusCode}"); return false; } private static void GenerateTestFiles(int partCount) { for (int i = 1; i <= partCount; i++) { var filePath = $"part{i}.png"; using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write); var randomData = new byte[5 * 1024 * 1024]; // 5 MB of random data new Random().NextBytes(randomData); fileStream.Write(randomData, 0, randomData.Length); } Console.WriteLine($"Generated {partCount} test files."); } } public class MultipartUploadInitializeResponse { public string UploadId { get; init; } = default!; public string Key { get; init; } = default!; public string Name { get; init; } = default!; public List<string> Urls { get; init; } = default!; }