18

I have an ASP.NET MVC 4 Web Api controller method that gets passed a list of file IDs and returns thumbnail images for those files.

So, the client might pass in a list of numeric IDs (e.g. 10, 303, 29), and the method returns a List where a ThumbnailImage looks a bit like this:

class ThumbnailImage
{
    public int Id { get; set; }
    // Some other stuff
    public byte[] RawData { get; set; }
}

The reason that the caller passes in a list of IDs rather than making one call per item should hopefully be obvious - there may be dozens or hundreds of items to download, and I'm trying to avoid all the HTTP traffic that would be required to download them individually.

Currently, I'm using RestSharp and JSON.NET, and so my ThumbnailImage objects are being passed across the wire as JSON. It's fine from a simplicity-of-coding point of view, but JSON is not an efficient way to represent that binary data.

So, I'm thinking that I should return the raw bytes as an octet-stream... however, while I can easily do that for a single image, I'm not sure of the best way to do it for multiple images, especially when I also need to return the ID and miscellaneous other information for each file. (The ID is required since the results will not necessarily be returned in a given order - and some files may be missing).

I could simply write everything piecemeal into the response stream, so that for each item I write the ID (suitably encoded), followed by the length of the image data, followed by the image data itself, and then followed by the same thing for the next item, etc.

The caller would then simply keep reading from the stream until it was exhausted, making assumptions about the encoding (and length!) of the IDs, etc.

I think that would work, but it seems clunky - is there a better way?

Gary McGill
  • 26,400
  • 25
  • 118
  • 202
  • How will the client use the images? Are the images for display in a web page? Or to download as an archive? Something else? – EBarr Sep 04 '12 at 16:58
  • Wondering if you can send the response as a MultipartContent where the inner contents could be StreamContent having the raw stream of bytes. – Kiran Sep 04 '12 at 18:57
  • @EBarr: I'm writing the client application too, so it's a closed environment. I'll be displaying the images within a WinForms application (as search result thumbnails). – Gary McGill Sep 04 '12 at 20:24
  • @KiranChalla: MultipartContent sounds like exactly what I'm looking for. I'd not come across it before. Hopefully I'll be able to find a tutorial, since the documentation is, er, scant. – Gary McGill Sep 04 '12 at 20:28

3 Answers3

18

OK, here's a snippet of code that seems to work, using the MultipartContent that KiranChalla referred to. (This is just a dummy sample that shows how to return two files of different types, in conjunction with a JSON-encoded "object" (which in this case is just a list of integer IDs).

public HttpResponseMessage Get()
{
    var content = new MultipartContent();
    var ids = new List<int>() { 1, 2 };

    var objectContent = new ObjectContent<List<int>>(ids, new System.Net.Http.Formatting.JsonMediaTypeFormatter());
    content.Add(objectContent);

    var file1Content = new StreamContent(new FileStream(@"c:\temp\desert.jpg", FileMode.Open));
    file1Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("image/jpeg");
    content.Add(file1Content);

    var file2Content = new StreamContent(new FileStream(@"c:\temp\test.txt", FileMode.Open));
    file2Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/plain");
    content.Add(file2Content);

    var response = new HttpResponseMessage();
    response.Content = content;
    return response;
}
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Gary McGill
  • 26,400
  • 25
  • 118
  • 202
3

You could create a compressed file (e.g. a ZIP file) out of all the thumbnails and send that back.

Then the caller just has to unzip it their end - sending a single file containing multiple files is going to be far more acceptable then sending multiple files in a single stream.

Disadvantage is you're less likely to be able to take advantage of caching (depending on your usage patterns of course).

RB.
  • 36,301
  • 12
  • 91
  • 131
  • But then I still have the same problem (albeit in a different place) of passing all the related information (e.g. the ID)... Also, I don't really see why it's any easier to unpack a ZIP than it is to unpack a concatenated stream of files? – Gary McGill Sep 04 '12 at 15:25
  • 3
    @GaryMcGill `ZIP` is widely implemented. It does not just compress, it also acts as a streamable container format for multiple files. You can have a "file" with related information as your first file and use the IDs of the actual files as file names. If you use ZIP's `Store` compression mode, it will effectively be the `name,length,bytes` scheme you described in the question. – M.Stramm Jul 06 '17 at 20:26
1

One challenge I see is that based on the number of images being sent back the caller has to adjust their timeout value. If this was for a book store, there could be a lot of images sent back.

What if you only sent back the urls for each image and leave it up to the caller to get the actual image? It would mean a little more traffic with multiple calls but the caller would get back information sooner than later and then get the images based on the caller's requirement.

I could be wrong, but I thought the idea behind rest was to identify each resource versus bundling a bunch of images and calling that a resource. Just a thought...

SainathDK
  • 62
  • 7
  • I agree - it's probably not very RESTful to bundle things up like this, but actually I'm also writing the (only) application that will use this web service, so I think that's OK. – Gary McGill Sep 04 '12 at 16:53
  • 1
    Good point about the time-out value, although since I'm also writing the client, I can also deal with that. (I'll probably only ever request a dozen or so at a time). I do think that the extra traffic required to send 100 images is too much to bear, and so fetching them individually doesn't sound right to me. – Gary McGill Sep 04 '12 at 16:55