Статьи

Создание клиента Imgur для Windows Phone — Часть 5 — Загрузка изображения

Если вы пропустили предыдущие четыре части, вы можете легко найти их здесь:


Вероятно, самая интересная часть о клиенте Imgur, который мы создаем, — это его способность загружать изображения наряду с возможностью визуализировать основной ассортимент галереи.
Существует специальная конечная точка, которая была разработана для выполнения этой задачи — 
/ image .

Прежде чем углубляться в механизм загрузки, давайте вспомним, как пользователь может выбрать изображение на устройстве Windows Phone. Чтобы читать фотографии с устройства, вам нужно использовать  PhotoChoserTask . Более того, это также позволит пользователю сделать новый снимок, если это необходимо. 

В моем приложении я просто добавил кнопку «Загрузить» и использовал следующий фрагмент для преобразования возвращенного изображения в двоичное содержимое:

PhotoChooserTask task = new PhotoChooserTask() { ShowCamera = true };
task.Completed += (s,d) =>
    {
        if (d.TaskResult == TaskResult.OK)
        {                 
            byte[] buffer = new byte[16*1024];

            using (MemoryStream stream = new MemoryStream())
            {
                int read = 0;

                while ((read = d.ChosenPhoto.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, read);
                }

                App.ServiceClient.UploadImage(stream.ToArray(), "WP Imagine Test", "Test");
            }
        }
    };
task.Show();

Это передаст массив, созданный из потока изображений, в функцию UploadImage, которая является моей реализацией механизма загрузки. Посмотрите на это:

public void UploadImage(byte[] content, string title, 
    string description, Action<bool> onCompletion = null)
{
    string BOUNDARY = Guid.NewGuid().ToString();
    string HEADER = string.Format("--{0}", BOUNDARY);
    string FOOTER = string.Format("--{0}--", BOUNDARY);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.imgur.com/3/upload");
    request.Method = "POST";
    request.Headers["Authorization"] = "Client-ID " + _clientID;
    request.ContentType = "multipart/form-data, boundary=" + BOUNDARY;

    StringBuilder builder = new StringBuilder();
    string base64string = Convert.ToBase64String(content);

    builder.AppendLine(HEADER);
    builder.AppendLine("Content-Disposition: form-data; name=\"image\"");
    builder.AppendLine();
    builder.AppendLine(base64string);
    builder.AppendLine(FOOTER);

    byte[] bData = Encoding.UTF8.GetBytes(builder.ToString());

    request.BeginGetRequestStream((result) =>
        {
            using (Stream s = request.EndGetRequestStream(result))
            {
                s.Write(bData, 0, bData.Length);
            }

            request.BeginGetResponse((respResult) =>
                {
                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(respResult);
                        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                        {
                            Debug.WriteLine(reader.ReadToEnd());
                        }
                    }
                    catch (WebException ex)
                    {
                        using (StreamReader reader = new StreamReader(ex.Response.GetResponseStream()))
                        {
                            Debug.WriteLine(reader.ReadToEnd());
                        }
                    }
                }, null);
        }, null);

}

Это может показаться довольно длинным, но на самом деле он выполняет довольно простую задачу —  создает  POST-запрос multipart / form-data . Обратите внимание, что в отличие от обычного подхода представление содержимого изображения в base64 не исключается — при создании многочастного POST-запроса экранирование строки приведет к сбою вызова API, поэтому убедитесь, что вы этого не делаете. Кроме того, вам все равно нужно будет передать свой идентификатор клиента, даже если вы можете загрузить изображение анонимно (без привязки к определенной учетной записи пользователя).

Способ, которым этот метод работает в настоящее время, будет загружать только изображение без метаданных, связанных с ним, таких как заголовок и описание для него. Давайте исправим это и добавим условные выражения, которые бы проверяли, должны ли заголовок и описание быть прикреплены к запросу.

if (!string.IsNullOrWhiteSpace(title))
{
    builder.AppendLine(HEADER);
    builder.AppendLine("Content-Disposition: form-data; name=\"title\"");
    builder.AppendLine();
    builder.AppendLine(title);
}

if (!string.IsNullOrWhiteSpace(description))
{
    builder.AppendLine(HEADER);
    builder.AppendLine("Content-Disposition: form-data; name=\"description\"");
    builder.AppendLine();
    builder.AppendLine(description);
}

Еще раз, мы следуем стандартным требованиям multipart / form-data для добавления дополнительных параметров. Как только изображение будет загружено, вы сможете увидеть заголовок и описание на странице изображения:

Если загрузка прошла успешно, мы можем ожидать получения следующей информации:

  1. ID изображения
  2. Удалить хеш
  3. Полный URL изображения


Их можно легко десериализовать, поэтому я легко могу использовать существующую
модель
Image , просто добавив
свойство
DeleteHash и связав его с
полем
deletehash в возвращаемой строке JSON.

public class ImgurImage
{
    public string ID { get; set; }
    [JsonProperty(PropertyName="deletehash")]
    public string DeleteHash { get; set; }
    public string Title { get; set; }
    public Int64 DateTime { get; set; }
    public string Type { get; set; }
    [JsonProperty(PropertyName = "animated")]
    public bool IsAnimated { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public Int64 Size { get; set; }
    public Int64 Views { get; set; }
    [JsonProperty(PropertyName = "account_url")]
    public string AccountUrl { get; set; }
    public string Link { get; set; }
    public string Bandwidth { get; set; }
    public int Ups { get; set; }
    public int Downs { get; set; }
    public int Score { get; set; }
    [JsonProperty(PropertyName = "is_album")]
    public bool IsAlbum { get; set; }
}

Когда изображение возвращается, мы больше не имеем дело с массивом изображений, поэтому модель ImgurImageData может быть не такой подходящей. Вместо этого я создал класс ImgurAtomicImageData :

using Newtonsoft.Json;

namespace Imagine.ImgurAPI
{
    class ImgurAtomicImageData
    {
        [JsonProperty(PropertyName = "data")]
        public ImgurImage Image { get; set; }
        public bool Success { get; set; }
        public int Status { get; set; }
    }
}

Когда ответ получен, я могу использовать общий  DeserializeObject  для ImgurAtomicImageData :

HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(respResult);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
    string jsonContent = reader.ReadToEnd();
    var imageData = JsonConvert.DeserializeObject<ImgurAtomicImageData>(jsonContent);
    Debug.WriteLine(jsonContent);
}

В результате вы сможете получить как HTTP-код, так и базовую модель изображения, содержащую все три поля данных, которые я упоминал выше.

Поздравляем, теперь вы можете загружать изображения в Imgur со своего Windows Phone!