Загрузка/загрузка байт-массивов с помощью AngularJS и ASP.NET Web API

Я провел несколько дней, исследуя и работая над решением для загрузки/загрузки байта [] s. Я близок, но имею одну оставшуюся проблему, которая, кажется, находится в моем блоке кода AngularJS.

Есть аналогичный вопрос о SO, но он не имеет ответов. См. https://stackoverflow.com/info/23849665/web-api-accept-and-post-byte-array

Вот некоторая справочная информация для установки контекста, прежде чем я укажу свою проблему.

  • Я пытаюсь создать клиентский/серверный интерфейс общего назначения для загрузки и загрузки байтов [] s, которые используются как часть запатентованной базы данных сервера.
  • Я использую TypeScript, AngularJS, JavaScript и Bootstrap CSS для создания одностраничного приложения (SPA).
  • Я использую ASP.NET Web API/С# на сервере.

SPA разрабатывается для замены существующего продукта, который был разработан в Silverlight, поэтому он ограничен существующими системными требованиями. SPA также должен ориентироваться на широкий спектр устройств (мобильных на настольные) и основных ОС.

С помощью нескольких онлайн-ресурсов (перечисленных ниже) я получил большую часть моего кода. Я использую асинхронный мультимедийный форматтер для байта [] s из ссылки Byte Rot ниже.

http://byterot.blogspot.com/2012/04/aspnet-web-api-series-part-5.html

Возврат двоичного файла с контроллера в веб-API ASP.NET

  • Я использую jpeg, преобразованный в Uint8Array, как мой тестовый пример на клиенте.
  • Реальные системные массивы байтов будут содержать смешанный контент, сжатый в предопределенные пакеты данных. Тем не менее, мне нужно иметь возможность обрабатывать любой действительный массив байтов, поэтому изображение является допустимым тестовым случаем.
  • Данные передаются на сервер правильно, используя код клиента и сервера, показанный ниже. И Батарейщик Rotter (не показан, но доступен на их веб-сайте).
  • Я проверил, что jpeg получен правильно на сервере в виде байта [] вместе с метаданных параметров строки.
  • Я использовал Fiddler для проверки того, что правильный ответ отправляется клиенту.
    • Правильный размер.
    • Изображение можно просмотреть в Fiddler.
  • Моя проблема в том, что ответ сервера в коде клиента Angular, приведенный ниже, неверен.

    • Неверным, я имею в виду неправильный размер (~ 10K против ~ 27.5K), и он не распознан как допустимое значение для конструктора UintArray. Visual Studio показывает JFIF, когда я помещаю курсор над возвращаемым "ответом", указанным ниже в коде клиента, но нет другого видимого индикатора содержимого.

/********************** Код сервера ********************** **/ Добавлен недостающий элемент в код после [FromBody] byte []

public class ItemUploadController : ApiController{ 
[AcceptVerbs("Post")]
    public HttpResponseMessage Upload(string var1, string var2, [FromBody]byte[] item){
        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new MemoryStream(item);
        result.Content = new StreamContent(stream);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        return result;
    }
}

/***************** Пример кода клиента ********************/

Единственное, что я пропустил из кода, это фактические параметры переменных.

$http({
url: 'api/ItemUpload/Upload',
    method: 'POST',
    headers: { 'Content-Type': 'application/octet-stream' },// Added per Byte Rot blog...
    params: {
        // Other params here, including string metadata about uploads
        var1: var1,
        var2: var2
    },
data: new Uint8Array(item),
// arrybuffer must be lowecase. Once changed, it fixed my problem.
responseType: 'arraybuffer',// Added per http://www.html5rocks.com/en/tutorials/file/xhr2/
transformRequest: [],
})
.success((response, status) => {
    if (status === 200) {
        // The response variable length is about 10K, whereas the correct Fiddler size is ~27.5K.
        // The error that I receive here is that the constructor argument in invalid.
        // My guess is that I am doing something incorrectly with the AngularJS code, but I
        // have implemented everything that I have read about. Any thoughts???
        var unsigned8Int = new Uint8Array(response);
        // For the test case, I want to convert the byte array back to a base64 encoded string
        // before verifying with the original source that was used to generate the byte[] upload.
        var b64Encoded = btoa(String.fromCharCode.apply(null, unsigned8Int));
        callback(b64Encoded);
    }
})
.error((data, status) => {
    console.log('[ERROR] Status Code:' + status);
});

/********************************************** ******************/

Любая помощь или предложения будут очень благодарны.

Спасибо...

Отредактировано, чтобы включить больше диагностических данных

Во-первых, я использовал функцию angular.isArray, чтобы определить, что значение ответа НЕ является массивом, который, я думаю, должен быть.

Во-вторых, я использовал следующий код для опроса ответа, который, как представляется, является невидимой строкой. Ведущие символы, похоже, не соответствуют какой-либо допустимой последовательности в коде байтового массива изображений.

var buffer = new ArrayBuffer(response.length);
var data = new Uint8Array(buffer);
var len = data.length, i;
for (i = 0; i < len; i++) {
    data[i] = response[i].charCodeAt(0);
}

Результаты эксперимента

Я провел эксперимент, создав значения байтового массива от 0 до 255 на сервере, которые я загрузил. Клиент AngularJS правильно принял первые 128 байтов (т.е. 0,1,..., 126,127), но остальные значения были 65535 в Internet Explorer 11 и 65533 в Chrome и Firefox. Fiddler показывает, что 256 значений были отправлены по сети, но в коде клиента AngularJS имеется только 217 символов. Если я использую только 0-127 в качестве значений сервера, все работает. Я понятия не имею, что может вызвать это, но ответ клиента больше соответствует подписанным байтам, что, я думаю, не возможно.

Данные Fiddler Hex с сервера отображают 256 байтов со значениями от 00,01,..., EF, FF, что является правильным. Как я упоминал ранее, я могу вернуть изображение и правильно его просмотреть в Fiddler, поэтому интерфейс веб-интерфейса API работает как для POST, так и для GET.

Я пытаюсь использовать XMLHttpRequest ваниль, чтобы увидеть, что я могу заставить это работать вне среды AngularJS.

Обновление тестирования XMLHttpRequest

Я смог подтвердить, что vanilla XMLHttpRequest работает с сервером для GET и способен возвращать правильные байтовые коды и тестовое изображение.

Хорошей новостью является то, что я могу взломать AngularJS, чтобы заставить мою систему работать, но плохая новость заключается в том, что мне не нравится это делать. Я бы предпочел остаться с Angular для всей моей клиентской серверной связи.

Я собираюсь открыть отдельную проблему в Stack Overflow, которая касается только вопросов GET byte [], которые у меня есть с AngularJS. Если я смогу получить разрешение, я обновлю эту проблему с помощью решения в исторических целях, чтобы помочь другим.

Обновление

Эрик Эслингер из Группы Google отправил мне небольшой сегмент кода, в котором подчеркивается, что responseType должен быть "arraybuffer", все в нижнем регистре. Я обновил блок кода выше, чтобы показать строчное значение и добавил примечание.

Спасибо...

Ответ 1

Наконец-то я получил ответ от Эрика Эслингера в Google Group. Он отметил, что использует

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

Он упомянул, что камешка была, вероятно, значительной, какой она есть. Изменен один символ, и весь поток работает сейчас.

Весь кредит принадлежит Эрику Эслингеру.