0

I need to load a picture from a web api backend into my Xamarin.Forms app. The picture is stored in an Azure Blob Storage.

This is my Web Api method:

[HttpGet("{id}")]
public HttpResponseMessage Get(int id)
{
    // Retrieve storage account from connection string.
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse("ConnectionString");

    // Create the blob client.
    CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

    // Retrieve reference to a previously created container.
    CloudBlobContainer container = blobClient.GetContainerReference("picturecontainer");

    // Retrieve reference to a blob named "photo.jpg".
    CloudBlockBlob blockBlob = container.GetBlockBlobReference("picture");

    var stream = new MemoryStream();

    blockBlob.DownloadToStream(stream);

    Image image = Image.FromStream(stream);
    MemoryStream memoryStream = new MemoryStream();
    image.Save(memoryStream, ImageFormat.Jpeg);

    HttpResponseMessage result = new    HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new ByteArrayContent(memoryStream.ToArray());

    result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");

    return result;
}

In my app I try to download the image bytes with the following code:

public App ()
{
    _client = new HttpClient();
    _client.MaxResponseContentBufferSize = 256000;

    Button downloadImageBtn = new Button () {
        Text = "Download Image",
    };
    var image = new Image() {
        Source = ImageSource.FromUri (new Uri ("http://www.engraversnetwork.com/files/placeholder.jpg")),
        Aspect = Aspect.AspectFit
    };
    downloadImageBtn.Clicked += async (object sender, EventArgs e) => {
        var values = await handleClick (sender, e);
        uploadPicButton.Text = values;
        var imageBytes = await downloadPicture();
        image.Source = ImageSource.FromStream(() => new MemoryStream(imageBytes));
    };

    MainPage = new ContentPage {
        Content = new StackLayout {
            VerticalOptions = LayoutOptions.Center,
            Children = {
                image,
                downloadImageBtn
            }
        }
    };
}

private async Task<byte[]> downloadPicture()
{
    var uri = new Uri (string.Format (RestUrl, "5"));
    //return await _client.GetByteArrayAsync (uri);
    var response = await _client.GetAsync (uri);
    if (response.IsSuccessStatusCode) {
        var content = await response.Content.ReadAsByteArrayAsync ();
        return content;
    }
    throw new HttpRequestException ();
}

However when I click on the button, the placeholder image disappears. Is there a problem when sending the image from the server or when receiving it in the app?

4
  • You don't show the code of downloadPicture, so I don't know whether the problem is inside this function. Try to debug. Put a breakpoint after you get imageBytes. How many bytes are there? Is it null or empty by any chance? Does it correspond to your picture's size? Commented Mar 11, 2016 at 11:57
  • @Grisha I added the downloadPicture() code to the questions. However, when the picture is send from the server the content body has arround 170k bytes, but there are only 265 returned image bytes. Commented Mar 11, 2016 at 13:59
  • Ok, we are closer. Now check on server side, in Get function, how many bytes do you send. Check result.Content. Commented Mar 11, 2016 at 15:56
  • @Grisha Like I said in the previous comment, the size of the result.Content int the Get function is about 170k bytes Commented Mar 12, 2016 at 8:48

2 Answers 2

1

I'd not implement downloading Images manually.

If you want to load more images and/or allow to display a placeholder while loading, I recommend the FFImageLoading library. It offers nice functionality like downloading, caching, showing placeholder and error images and most important: down sampling the image to the target size. This is always a pain on android. Its available for native UI xamarin projects and for Xamarin.Froms.

You can bind strings that contain urls directly to the Source. The code would like like:

<ffimageloading:CachedImage 
    Source="http://thecatapi.com/?id=MTQ5MzcyNA">
</ffimageloading:CachedImage>
Sign up to request clarification or add additional context in comments.

Comments

0

After blockblob.DownloadToStream line, try to set Position of the stream to 0. I'm not familiar with Azure API, but I think it may help.

Also ,try to use ReadAsStreamAsync instead of ReadAsByteArrayAsync in your downloadPicture function. Something like this:

var responseStream = await response.Content.ReadAsStreamAsync();
byte[] buf = new byte[512];
int bufSize;
while((bufSize = (await responseStream.ReadAsync(buf, 0, buf.Length))) > 0)
{
    // store the received bytes to some buffer until the file is fully downloaded
}

Do you get all your content this way?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.