24

I'm trying to send some images + some meta data to a server by HTTP post from a windows store app but get stuck when trying to actually include the data in the post. It cannot be done the way you would accomplish this in a windows forms app or similar due to the changes to the store app API.

I get the error.

cannot convert source type byte[] to target type System.Net.Http.httpContent

now this is obviously because it's 2 different types that can't be implicitly casted, but it's basically what I'm looking to be able to do. How do I make get my byte array data into the httpContent type so I can include it in the following call

httpClient.PostAsync(Uri uri,HttpContent content);

here's my full upload method:

async private Task UploadPhotos(List<Photo> photoCollection, string recipient, string format)
    {
        PhotoDataGroupDTO photoGroupDTO = PhotoSessionMapper.Map(photoCollection);

        try
        {
            var client = new HttpClient();
            client.MaxResponseContentBufferSize = 256000;
            client.DefaultRequestHeaders.Add("Upload", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");

            // POST action_begin
            const string actionBeginUri = "http://localhost:51139/PhotoService.axd?action=Begin";
            HttpResponseMessage response = await client.GetAsync(actionBeginUri);
            response.EnsureSuccessStatusCode();
            string responseBodyAsText = await response.Content.ReadAsStringAsync();
            string id = responseBodyAsText;
            ////

            // POST action_upload
            Uri actionUploadUri = new Uri("http://localhost:51139/PhotoService.axd?action=Upload&brand={0}&id={1}&name={2}.jpg");

            var metaData = new Dictionary<string, string>()
            {
                {"Id", id},
                {"Brand", "M3rror"}, //TODO: Denne tekst skal komme fra en konfigurationsfil.
                {"Format", format},
                {"Recipient", recipient}
            };

            string stringData = "";
            foreach (string key in metaData.Keys)
            {
                string value;
                metaData.TryGetValue(key, out value);
                stringData += key + "=" + value + ",";
            }

            UTF8Encoding encoding = new UTF8Encoding();
            byte[] byteData = encoding.GetBytes(stringData);

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, actionUploadUri);

            // send meta data
            // TODO get byte data in as content
            HttpContent metaDataContent = byteData;
            HttpResponseMessage actionUploadResponse = await client.PostAsync(actionUploadUri, metaDataContent);

            actionUploadResponse.EnsureSuccessStatusCode();
            responseBodyAsText = await actionUploadResponse.Content.ReadAsStringAsync();

            // send photos
            // TODO get byte data in as content
            foreach (byte[] imageData in photoGroupDTO.PhotosData)
            {
                HttpContent imageContent = imageData;
                actionUploadResponse = await client.PostAsync(actionUploadUri, imageContent);
                actionUploadResponse.EnsureSuccessStatusCode();
                responseBodyAsText = await actionUploadResponse.Content.ReadAsStringAsync();
            }                
            ////

            // POST action_complete
            const string actionCompleteUri = "http://localhost:51139/PhotoService.axd?action=Complete";
            HttpResponseMessage actionCompleteResponse = await client.GetAsync(actionCompleteUri);
            actionCompleteResponse.EnsureSuccessStatusCode();
            responseBodyAsText = await actionCompleteResponse.Content.ReadAsStringAsync();
            ////
        }

        catch (HttpRequestException e)
        {
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
        }
    }
Essah
  • 899
  • 2
  • 8
  • 20
  • 1
    Doesn't the binary data need to be *serialised*? Also you probably want to *stream* it rather than upload it all at once. http://stackoverflow.com/questions/19005991/webapi-how-to-handle-images may help. – bytefire May 07 '14 at 13:21

3 Answers3

65

It will be more straightforward to use System.Net.Http.ByteArrayContent. E.g:

// Converting byte[] into System.Net.Http.HttpContent.
byte[] data = new byte[] { 1, 2, 3, 4, 5};
ByteArrayContent byteContent = new ByteArrayContent(data);
HttpResponseMessage response = await client.PostAsync(uri, byteContent);

For text only with an specific text encoding use:

// Convert string into System.Net.Http.HttpContent using UTF-8 encoding.
StringContent stringContent = new StringContent(
    "blah blah",
    System.Text.Encoding.UTF8);
HttpResponseMessage response = await client.PostAsync(uri, stringContent);

Or as you mentioned above, for text and images using multipart/form-data:

// Send binary data and string data in a single request.
MultipartFormDataContent multipartContent = new MultipartFormDataContent();
multipartContent.Add(byteContent);
multipartContent.Add(stringContent);
HttpResponseMessage response = await client.PostAsync(uri, multipartContent);
kiewic
  • 15,852
  • 13
  • 78
  • 101
  • Which is fast enough, secure to process the data which contains the text and numbers combination (around 1lakh records in a list-json serialized)? ByteArrayContent or StringContent. Please suggest – Ganesh Feb 06 '18 at 15:22
  • I think the variable name above should be "response" not "reponse", I was unable to edit it. Caused me issues for a few minutes when copying / pasting into existing code, might save someone else some time if the code is updated with the correct spelling? – Tim Newton Jan 09 '23 at 11:42
12

You need to wrap the byte array in an HttpContent type.

If you are using System,Net.Http.HttpClient:

HttpContent metaDataContent = new ByteArrayContent(byteData);

If you are using the preferred Windows.Web.Http.HttpClient:

Stream stream = new MemoryStream(byteData);
HttpContent metaDataContent = new HttpStreamContent(stream.AsInputStream());
Jon
  • 2,891
  • 2
  • 17
  • 15
  • 2
    What should be the server-side action pattern for accepting a `ByteArrayContent`? – Shimmy Weitzhandler Dec 11 '17 at 08:38
  • In my case, I'm processing a file uploaded on the server in order to send it to another server's REST API. The use of ByteArrayContent doesn't work. The REST server just gives a status 200 empty reply. However, if I write the uploaded data to a file on the disk and then use the file for a StreamContent, it works. I wonder why. – cdup Aug 02 '22 at 12:39
1

The concept you are looking for is called Serialization. Serialization means preparing your data (which could be heterogeneous and without a predefined strucutre) for storage or transmission. Then, when you need to use the data again, you do the opposite operation, deserialization, and get back the original data structure. The link above shows a few methods on how this could be done in C#.

Ermir
  • 1,391
  • 11
  • 25
  • Nice hint, thanks. Following one answer elsewhere I was decoding form content using UTF-8. Turns out it was being sent as Base64, so that's what I needed to use. – Stuart Aitken Aug 14 '19 at 08:47