2019年5月21日 星期二

將圖片檔案上傳到遠端伺服器

將圖片檔案上傳到遠端伺服器

由於最近正在進行 Xamarin.Forms 的拍照與檔案上傳的練習,因此,特別設計一個 RESTful 上傳圖片的 API,不過,該 API 是不需要經過身分驗證,而是可以直接將圖片檔案上傳到遠端伺服器,所以,撰寫出一篇文章,可以使用 Console 類型的專案,將一個特定的圖片檔案,使用 HttpClient 配合 Multipart 編碼方式,將圖片檔案上傳到遠端伺服器上,若成功上傳之後,將會取得該圖片的 URL 網址,最後,可以透過該 URL 網址來取得該圖片。不過,該上傳 URL 僅用於練習之用,因此,上傳上來的圖片將會隨時有可能的不經通告,就會直接刪除。
在這篇文章所提到的範例程式碼,可以從這裡 GitHub 取得
首先,先要設計呼叫這個上傳圖片 Web API 會用到的相關類別,因為這個練習用的 ASP.NET Core 設計的 Web API 專案,是遵循 REST 規範而設計出來的,因此,當呼叫這台伺服器上的 RESTful Web API 的時候,都會回傳 APIResult 格式的 JSON 物件,透過該物件可以了解到此次呼叫 Web API 是否成功或者失敗,失敗的原因為何?而該失敗原因的說明內容,將會定義在遠端伺服器上的錯誤訊息對應表中。
若此次呼叫 Web API 成功,則可以從 APIResult.Payload 這個屬性中取得遠端伺服器回傳的 JSON 物件內容,現在,便可以根據所呼叫的 Web API 當初設計的規格,使用相對應的類別,反序列化該 JSON 物件成為 .NET 物件即可;最後,透過 UploadImageResponseDTO.ImageUrl 屬性,便可以得到該圖片在遠端伺服器上的 URL 囉。
C Sharp / C#
/// <summary>
/// 呼叫 API 回傳的制式格式
/// </summary>
public class APIResult
{
    /// <summary>
    /// 此次呼叫 API 是否成功
    /// </summary>
    public bool Status { get; set; } = true;
    public int HTTPStatus { get; set; } = 200;
    public int ErrorCode { get; set; }
    /// <summary>
    /// 呼叫 API 失敗的錯誤訊息
    /// </summary>
    public string Message { get; set; } = "";
    /// <summary>
    /// 呼叫此API所得到的其他內容
    /// </summary>
    public object Payload { get; set; }
}
public class UploadImageResponseDTO
{
    public string ImageUrl { get; set; }
}
當要進行圖片上傳的時候,當然需要先取得該圖片檔案,在這裡需要取得該圖片檔案的絕對路徑名稱(含檔案名稱) imageFileName 、該圖片檔案的名稱(沒有路徑) fileName 。
現在,可以建立一個 HttpClient 物件,這個練習專案使用的上傳圖片 API URL 為 http://lobworkshop.azurewebsites.net/api/UploadImage 。
為了要能夠上傳圖片檔案,不是一般的文字內容,而是二進位 Binary 的內容,此時,需要使用 MultipartFormDataContent 類別,將二進位的圖片檔案進行編碼,這樣就可以上傳到遠端伺服器上,而遠端伺服器上也可以透過相同的編碼方式,還原到原先的圖片檔案,對於這樣的編碼規範,可以參考 https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html 。當要上傳圖片的時候,將會使用 StreamContent 這個類別,取得該上傳檔案的 Stream 物件,便可以得到該圖片的檔案內容。
當完成圖片檔案的編碼物件產生之後,就可以使用 await client.PostAsync(url, content); 將該圖片上傳到遠端伺服器上。
當呼叫完成上傳圖片 API 之後,將會得到一個 JSON 文字物件,這個時候僅需使用 JsonConvert.DeserializeObject 將這個 JSON 物件反序列化成為 .NET 物件,這裡使用的方法將會是強型別的泛型方法。最後,上傳圖片的網址可以透過 uploadImageResponseDTO.ImageUrl 屬性來取得。
C Sharp / C#
class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("使用 HttpClient 上傳檔案範例");
        string path = Directory.GetCurrentDirectory();
        string fileName = "XamarinForms.jpg";
        string imageFileName = Path.Combine(path, fileName);

        HttpClient client = new HttpClient();
        string url = $"http://lobworkshop.azurewebsites.net/api/UploadImage";
        #region 將圖片檔案,上傳到網路伺服器上(使用 Multipart 的規範)
        // 規格說明請參考 https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
        using (var content = new MultipartFormDataContent())
        {
            // 開啟這個圖片檔案,並且讀取其內容
            using (var fs = File.Open(imageFileName, FileMode.Open))
            {
                var streamContent = new StreamContent(fs);
                streamContent.Headers.Add("Content-Type", "application/octet-stream");
                streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileName + "\"");
                content.Add(streamContent, "file", fileName);

                // 上傳到遠端伺服器上
                HttpResponseMessage response = await client.PostAsync(url, content);

                if (response != null)
                {
                    if (response.IsSuccessStatusCode == true)
                    {
                        // 取得呼叫完成 API 後的回報內容
                        String strResult = await response.Content.ReadAsStringAsync();
                        APIResult apiResult = JsonConvert.DeserializeObject<APIResult>(strResult, new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore });
                        if (apiResult?.Status == true)
                        {
                            UploadImageResponseDTO uploadImageResponseDTO = JsonConvert.DeserializeObject<UploadImageResponseDTO>(apiResult.Payload.ToString());
                            Console.WriteLine("上傳圖片的網址");
                            Console.WriteLine(uploadImageResponseDTO.ImageUrl);
                        }
                    }
                }
            }
        }
        #endregion

        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
}
C Sharp / C#
csharp



沒有留言:

張貼留言