7

In short, I want to send the binary data of the image to my handler that will get saved in the DB as a byte array. Reading the value of a file input using readAsBinaryString(f) in my Javascript, I'll get output like this:

GIF89a,úæÿÿÿ2c½3fÌ Smaäµééúþc«T.[ÈéùAtεÚõ[ãXßÆî*[µc³8Ûõüÿfj¥æ§ÈïÛå÷ËØñI}ÓQ×
*\»q£E}Ûÿå§ÓõþÿIÛv¤Þ´Åè«æ ³][us¬çAy×MÞ,a½«ÔóZÝL2äëùQ×(Eq<pË5V¨·ÏIÓ¨»åQßY¥3bØÈ
æ¬z³é<uÓ3£ÎñE¾á÷RÛR¢K­®ÎØØìÍAtÓÑÔØrÀ-hݪÑïôõüR|ÎäóÖUËåæçXÔw»^s®ëI}ÛQ}ÔEÛ·Îñ½Óêd»Ì
ÌëöåóôöÖàñE×Cr¿C¤3óúëLÍYÜ3fõûöÑðû Øûÿõw²ñ`ª»ßÀy|Á¿ÃIuÔM×ûñû{¹R4¼ìe¡äl«ç!ÿNETSCA
PE2.0!ÿXMP DataXMP<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpm
eta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02
/06-14:56:27   // etc..  

That data is sent via AJAX:

$.ajax({
    url: theUrl,
    type: 'POST',
    data: { Image: image } // image = data above
});

This is a binary string? When I send this string to my handler (IHttpHandler), in order to store it into a byte array, I may only get the bytes if I set the encoding to ISO-8859-1.

public void ProcessRequest (HttpContext aContext) 
{ 
    // This works as long as requestValidationMode = "2.0" in web.config
    // Is there a way to bypass HttpRequestValidationException  just on 
    // THIS data?
    byte[] imageBytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(_Context.Request["Image"]);

    //...
}

Is this normal? This also throws an HttpRequestValidationException (A potentially dangerous Request.Form value was detected from the..). I'd rather not change the requestValidationMode since it opens up to XSS, so how does one escape the binary string? Does Base64 encoding cover this, and if so, does converting from Base64 in the handler contain metadata about its data type?

Josh
  • 999
  • 3
  • 15
  • 31
  • There's another way at http://stackoverflow.com/questions/3436398/convert-a-binary-string-representation-to-a-byte-array – stuartd May 17 '13 at 15:59
  • Thanks for the response, stuartd. My issue there is my initial "binary string" isn't represented as 1s and 0s, it's as that above snippet, which obviously isn't binary, *I think*. Why does `readAsBinaryString` return that as *that*? Is that another form of binary I'm not familiar with? – Josh May 17 '13 at 16:01
  • That looks like `Assembly` to me. Which in and of itself, is odd. – Brian May 17 '13 at 16:04
  • Yes, that string is normal... If you're passing your string to a handler you should encode it to base64 and in the handler decode the data back... 1s and 0s are used in assembly... it's inefficient to pass data like that... – bastos.sergio May 17 '13 at 16:13
  • Fair enough. If I send it as base64 and decode it, and store as bytes array, will the final result include data's type? Such that when I retrieve the data in the future from DB, it will know whether it's png, gif, etc.? – Josh May 17 '13 at 16:17
  • Also, is there a method to *escape* the binary data from my post to bypass the HttpRequestValidationException? – Josh May 17 '13 at 16:18
  • no, you'll have to save the filename, filetype, filesize (normally not needed), etc... seperatly. As for your other question, base64 data should already be normalized against HttpRequestValidationException errors, so you won't have to do anything extra... – bastos.sergio May 17 '13 at 16:21
  • @bastos.sergio Please see my comment to your answer. – Josh May 17 '13 at 16:25
  • If you are worried about identifying binary data without explicitly knowing its original type, take a look at Magic Numbers here http://en.wikipedia.org/wiki/Magic_number_%28programming%29 – Zerkz May 17 '13 at 16:27

1 Answers1

9

It really helps to see what you are doing in code, that's why I asked you to show it. I guess your code looks like this:

var f = $("image").files[0];
var reader = new FileReader();
reader.readAsBinaryString(f);
var image = reader.result;

$.ajax({
    url: theUrl,
    type: 'POST',
    data: { Image: image }
});

Now when you say:

I'll get output like this:

GIF89a,úæÿÿÿ2c½3...

Is my output [...] binary data?

Well yes, but you can read it, so it is text. The difference between binary and textual data is a bit confusing, read this if you want to get more confused. The cause of your problem is encoding, as explained in that article.

It depends on how you output it to your page, but the browser may or may not apply a certain encoding to the data it receives (a Fiddler inspection can teach you more on what's being sent over the HTTP wire) and display it as more or less readable text.

This does not apply to your image variable though, which contains the actual, binary data from the readAsBinaryString() result, in the form of "raw binary data[sic]". Oh, loosely-typed, who cares what you return. I guess/hope a byte array. You now need to send this to a server. Preferably, file uploads are handled by <input type="file" /> elements, but sometimes you'll need to do things using AJAX. You can't really do real file uploads through JavaScript, although as far as I know browser support seems to be increasing.

So you'll need to POST with the file contents as one of the posted form's parameters. In order for this to happen successfully with binary data, you'll need to properly encode it for form submission.

First make sure the request is made with the content-type of application/x-www-form-urlencoded. You can check this in Fiddler or by consulting the manual. The latter does not mention any encoding at all, you have to figure that out.

Now you need to url-encode the binary data in order to post it. This function returns the input bytes, interpreted as UTF-8, as an URL-encoded string that can safely be posted. Then serverside, you can replace

System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(_Context.Request["Image"]);

With

System.Text.Encoding.UTF8.GetBytes(_Context.Request["Image"]);

But why do you have the file's contents in that form anyway? As this answer mentions, the FileReader also contains a readAsDataURL method, which allows you to use reader.result directly as POST variable.

So, your code becomes like this:

var f = $("image").files[0];
var reader = new FileReader();
reader.readAsDataURL(f);
var image = reader.result;

$.ajax({
    url: theUrl,
    type: 'POST',
    data: { Image: image }
});

Then serverside, you will have to decode the data from base 64:

byte[] imageBytes = System.Convert.FromBase64String(_Context.Request["Image"]);

How does that work?

Community
  • 1
  • 1
CodeCaster
  • 131,656
  • 19
  • 190
  • 236
  • +1 Thanks for the answer! Unfortunately, I've been instructed to send the binary data over the wire w/o base64 encoding it, because of its resulting inflated payload. I'm encoding the image on the front-end before sending it over. However what I find odd, but this may be due to my shallow understanding on the subject, is when I get the UTF8 encoded bytes on the server side, I'm left with a byte array length of 25, when the image is ~40kB. Without encoding and using as-is (from my OP), I'm left with a byte array length of ~40k, which is the size of the image, but the above exception arises. – Josh May 20 '13 at 14:44
  • I'm sorry, not 25 -- ~159k – Josh May 20 '13 at 15:07
  • After further investigation, I understand what is going on a little more. Encoding the binary data appears to be necessary, yes, but will unduly inflate the payload, thus the 159kB size. Is there a way to send the binary data as-is *without* encoding? Please know that we have security measures on the back-end that will make sure the image's binary data is valid. – Josh May 20 '13 at 15:29