1

I'm trying to upload a pdf file through javascript. I have the base64 representation of the file and I decode it. The file is uploaded but that data seems to be corrupt.

The error message says "The file is damaged and could not be repaired" when I try opening the file.

I get the base64 string server side like so...

byte[] buffer = new byte[length];  
var postedFile = httpRequest.Files[file];                 
postedFile.InputStream.Read(buffer, 0, length);
string encodedString = Convert.ToBase64String(buffer);

Here is the ajax call I make to upload the file

var data= Base64.decode(result); //result is the base64 encoded string
 $.ajax({
                    url: url,
                    type: "POST",
                    data: data,
                    processData: false,
                    headers: {
                        "accept": "application/json;odata=verbose",
                        "X-RequestDigest": _digetsValue,
                        "content-length": length

                    },
                    success: function (data)
                    {
                        alert("it worked");

                    },
                    error: function (err)
                    {

                    }
                });

For decoding, I'm using this chunk of code... or from here How can you encode a string to Base64 in JavaScript?

var Base64 = {
        // private property
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        // public method for encoding
        encode: function (input)
        {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            input = Base64._utf8_encode(input);

            while (i < input.length)
            {

                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2))
                {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3))
                {
                    enc4 = 64;
                }

                output = output +
                Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
                Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);

            }

            return output;
        },

        // public method for decoding
        decode: function (input)
        {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            while (i < input.length)
            {

                enc1 = Base64._keyStr.indexOf(input.charAt(i++));
                enc2 = Base64._keyStr.indexOf(input.charAt(i++));
                enc3 = Base64._keyStr.indexOf(input.charAt(i++));
                enc4 = Base64._keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64)
                {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64)
                {
                    output = output + String.fromCharCode(chr3);
                }

            }

            output = Base64._utf8_decode(output);

            return output;

        },

        // private method for UTF-8 encoding
        _utf8_encode: function (string)
        {
            string = string.replace(/\r\n/g, "\n");
            var utftext = "";

            for (var n = 0; n < string.length; n++)
            {

                var c = string.charCodeAt(n);

                if (c < 128)
                {
                    utftext += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048))
                {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else
                {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }

            }

            return utftext;
        },

        // private method for UTF-8 decoding
        _utf8_decode: function (utftext)
        {
            var string = "";
            var i = 0;
            var c = c1 = c2 = 0;

            while (i < utftext.length)
            {

                c = utftext.charCodeAt(i);

                if (c < 128)
                {
                    string += String.fromCharCode(c);
                    i++;
                }
                else if ((c > 191) && (c < 224))
                {
                    c2 = utftext.charCodeAt(i + 1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
                }
                else
                {
                    c2 = utftext.charCodeAt(i + 1);
                    c3 = utftext.charCodeAt(i + 2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }

            }
            return string;
        }
    }

Originally, I was doing something like this...

getFileBuffer = function (file)
{
    var deferred = $.Deferred();
    var reader = new FileReader();
    reader.onload = function (e)
    {
        deferred.resolve(e.target.result);
    }
    reader.onerror = function (e)
    {
        deferred.reject(e.target.error);
    }
    reader.readAsArrayBuffer(file);

    return deferred.promise();
};

But of course this is exclusive to HTML5 and whatnot. So when I would call my ajax function, I would pass in this arraybuffer from the file reader and voila! It would work. Now I'm trying to mimic the same sort of functionality with IE8. Is this possible?

Thanks

Community
  • 1
  • 1
prawn
  • 2,643
  • 2
  • 33
  • 49
  • (Possibly) minor typo: `_digetsValue` should be `_digestValue` – klugerama Oct 09 '13 at 17:18
  • 1
    I don't understand, why are you doing `Base64.decode`? That is _Base64 -> String_, but you don't have a _String_, you have a _File_, so of course it won't work. – Paul S. Oct 09 '13 at 17:19
  • The functions you're using are not binary-safe! If you read the comments on the page you got it from, you will see that it has been discussed. :) – roberto Oct 09 '13 at 17:21
  • so then I would have to get the raw binary data for the file? Is that possible in IE8? – prawn Oct 09 '13 at 17:22
  • @prawn show us how you're obtaining the file – Paul S. Oct 09 '13 at 17:22
  • I obtain the file server side in c#. Updated the question – prawn Oct 09 '13 at 17:26
  • With your latest edit, I became even more confused. :/ So, let's start from the beginning: you're trying to read a PDF file that exists on the user's computer and upload it to the server, right? – roberto Oct 09 '13 at 18:31
  • Haha, I'm sorry. I'll be the first to confess my ignorance when it comes to working with files through javascript. In essence, I acquired the base64 string from the server. And then I needed to somehow insert that file while working with that base64 string. Paul graciously provided some functions which provides an array of integers which was represented in the base64 string. I abandoned my RESTful approach and opted to use CSOM with that new integer array. and it seems to be working thus far. – prawn Oct 09 '13 at 18:55

1 Answers1

0

I wrote some functions a while ago I believe to be binary safe, here are the relevant ones for you; base64To6 and (perhaps poorly named) base6To8.

var chars = (
        'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
        'abcdefghijklmnopqrstuvwxyz' +
        '0123456789+/'
    ),
    inver = {}, i;
for (i = 0; i < chars.length; ++i) {
    inver[chars[i]] = i;
}

function base64To6(b64) {
    var arr6 = [],
        i = b64.length, lenMod = 0;
    while (b64.charAt(--i) === '=')
        ++lenMod;
    for (i = 0; i < b64.length - lenMod; ++i)
        arr6.push(inver[b64.charAt(i)]);
    i = b64.length & 3;
    if (i) i = 4 - i;
    i = i + b64.length;
    arr6.byteLength = 3 * i / 4 - lenMod;
    return arr6;
}

function base6To8(arr6) {
    var arr8 = [], i,
        e1, e2, e3,
        s1, s2, s3, s4,
        d1, d2, d3, d4;
    for (i = 0; i < arr6.length; i += 4) {
        s1 = (d1 = arr6[i]    ) & 63;
        s2 = (d2 = arr6[i + 1]) & 63;
        s3 = (d3 = arr6[i + 2]) & 63;
        s4 = (d4 = arr6[i + 3]) & 63;
        // xxxxxx xxyyyy yyyyzz zzzzzz
        e1 = ( s1       << 2) + (s2 >>> 4);
        e2 = ((s2 & 15) << 4) + (s3 >>> 2);
        e3 = ((s3 &  3) << 6) +  s4       ;
        arr8.push(e1);
        if (d3 !== undefined)
            arr8.push(e2, e3);
        else if (d2 !== undefined )
            arr8.push(e2);
    }
    if (arr6.byteLength !== undefined)
        arr8.length = +arr6.byteLength;
    return arr8;
}

Now if you do

var int8arr = base6to8(base64to6(data));

You will have an Array of Integers (at most 8 bit numbers), which the Base64 was representing. JavaScript does not use Int8 for Number so you can't immediately use this Array.

Unfortunately, as far as I know, IE8 does not support UInt8Array or Blob, which would let you then happily work with the binary data, so I'm not sure exactly where you'd want to take it from here.

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • 1
    Thank you for this. I promise to name all of my children Paul. Even the girls. But really, this worked for me. I used this in conjunction with the sharepoint csom to insert a file. Thank you! – prawn Oct 09 '13 at 18:43