简体   繁体   中英

AngularJS GET byte[] error when downloading binary data using ASP.NET Web API

For additional background information on this issue, see Uploading/Downloading Byte Arrays with AngularJS and ASP.NET Web API .

While working on a TypeScript/AngularJS(v1.2.22)/Web API solution for uploading/download byte arrays, I discovered a scenario where AngularJS does not respond correctly to a client-side GET request for byte array data. To prove out my server code, I created some separate XMLHttpRequest code outside of Angular that does work properly with the Web API server code.

  1. There may be something that I am still missing in the AngularJS code, but I have scoured numerous documents, articles and code examples and have not found anything that is obviously wrong with the code.
  2. I have verified that the data sent from the server through Fiddler is identical for the AngularJS and XMLHttpRequest code.
  3. The first issue with the Angular code is that it interprets the data as a string, NOT a byte array, while the XMLHttpRequest code interprets that code correctly as a byte array.
  4. The second issue is that the AngularJS code corrupts all values greater than 127 (ie, 128-255). I included a screen shot of this below.
  5. The third issue is that data is lost. The reconstructed data in the AngularJS code had only 217 bytes instead of 256 bytes.

Here is the XMLHttpRequest code that works. Note that the response is a valid argument for the UInt8Array constructor.

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/api/Values", true);
    // Worked on IE, Chrome, and Firefox with this line commented out, Firefox docs say it should be declared.
    oReq.overrideMimeType("charset=x-user-defined");
    oReq.responseType = "arraybuffer";
    oReq.onload = function () {
        var arrayBuffer = oReq.response, bArray = new Uint8Array(arrayBuffer), str = "";
        if (arrayBuffer) {
            for (var i = 0; i < bArray.length; i++) {
                str += bArray[i] + ((i < bArray.length - 1) ? "," : "");
            }
            window.alert("XMLHttpRequest GET Output: " + str);
        }
    };

Here is the AngularJS code that does not work correctly (ie, return a byte[] with the correct values). Note that the response had to be converted from a string to an array.

    $http({
        method: 'GET',
        url: 'api/values',
        headers: {'Content-Type': 'application/octet-stream; charset=x-user-defined'},
        // The issue was caused by using camel case B. MUST BE 'arraybuffer'
        // Changed code below to reflect correction.
        responseType: 'arraybuffer',
        transformResponse: []
    }).success(function (response, status) {
            if (status === 200) {// Issue 1: response is not byte[]. It is a string. Issue 2: String values > 127 are corrupted.
                var buffer = new ArrayBuffer(response.length), bArray = new Uint8Array(buffer), len = bArray.length, i, str = "";
                for (i = 0; i < len; i++) {// Cannot create an array from response data here, but I can using XMLHttpRequest.
                    str += bArray[i] = response[i].charCodeAt(0);// Have to read character value.
                    if (i < len - 1)
                        str += ",";
                }
                window.alert("Angular Output: " + str);
                var unsigned8Int = new Uint8Array(bArray);// Can create new array here after processing string values.
                var b64Encoded = btoa(String.fromCharCode.apply(null, unsigned8Int));
                callback(b64Encoded);
            }
        }).error((data, status) => {
            console.log('[ERROR] Status Code:' + status);
        });

I annotated the issues in the AngularJS code above. I tried many variations to the above code based upon various articles, but none worked. I tried the default transformResponse, but found one article that said the defaults had to be reset.

The server-side code is as follow:

    [AcceptVerbs("Get")]
    public HttpResponseMessage Get()
    {
        byte[] item = new byte[256];
        for (var i = 0; i < item.Length; i++)
        {
            item[i] = (byte)i;
        }
        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
        result.Content = new ByteArrayContent(item);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        return result;
    }

I checked the hex data in Fiddler for each test case below, and it was identical (ie, 000102...EFFF), and the length in Fiddler was 256. Inside the Angular code, the data length was only 217 versus 256, so data is being corrupted and lost.

Below are two screen shots of the two different GET code blocks above.

屏幕截图

I repeated the tests with an image injected in the server code instead of the 256 byte array. Again, the image data content (see below) and length (27,998 bytes) was identical in Fiddler.

获取图像响应

The received image inside the Angular code block had to be converted from a string to a byte array, and the size was much smaller (~20K).

There seems to be two possibilities: 1) My AngularJS code is bad, or 2) There is a problem with Angular.

I will attempt to dive into the AngularJS code in the debugger, but I am not sure how far I will get.

I could really use help with this from some of you Angular jocks.

Thanks...

Update Eric Eslinger just pointed out my error. arrayBuffer MUST BE arraybuffer (no camel)

The specific call to make is

$http.get('http://example.com', {responseType: 'arraybuffer'})

and you'll get a proper binary response.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM