AngularJS $http-post - преобразовать двоичный файл в excel и загрузить

Я создал приложение в Angular JS для загрузки книги Excel через сообщение $http.

В приведенном ниже коде я передаю информацию в форме JSON и отправлю ее на веб-службу REST сервера (java) через сообщение Angular $http. Веб-служба использует информацию из JSON и создает книгу Excel. В ответе в блоке успеха сообщения http $я получаю двоичные данные внутри этой переменной data​​strong > , но не знаю, как ее преобразовать и загрузить как файл Excel.

Может кто-нибудь скажет мне какое-нибудь решение для преобразования двоичного файла в Excel и загрузки?

Мой код приведен ниже:

$http({
        url: 'myweb.com/myrestService',
        method: "POST",
        data: json, //this is your json data string
        headers: {
           'Content-type': 'application/json'
        }
    }).success(function (data, status, headers, config) {

        // Here i'm getting excel sheet binary datas in 'data' 

    }).error(function (data, status, headers, config) {

    });

Ответ 1

Только что заметил, что вы не можете использовать его из-за IE8/9, но я все равно отправлю submit... возможно, кто-то найдет его полезным

Это можно сделать через браузер, используя blob. Обратите внимание на responseType и код в обещании success.

$http({
    url: 'your/webservice',
    method: "POST",
    data: json, //this is your json data string
    headers: {
       'Content-type': 'application/json'
    },
    responseType: 'arraybuffer'
}).success(function (data, status, headers, config) {
    var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
    var objectUrl = URL.createObjectURL(blob);
    window.open(objectUrl);
}).error(function (data, status, headers, config) {
    //upload failed
});

Есть некоторые проблемы с ним, хотя вроде:

  • Он не поддерживает IE 8 и 9:
  • Он открывает всплывающее окно, чтобы открыть objectUrl, который люди могли заблокировать
  • Создает странные имена файлов

Это сработало!

blob Код на стороне сервера в PHP, который я тестировал, выглядит так. Я уверен, что вы можете установить аналогичные заголовки в Java:

$file = "file.xlsx";
header('Content-disposition: attachment; filename='.$file);
header('Content-Length: ' . filesize($file));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Pragma: public');
echo json_encode(readfile($file));

Изменить 20.04.2016

Браузеры затрудняют сохранение данных таким образом. Один хороший вариант - использовать filesaver.js. Он обеспечивает реализацию кросс-браузера для saveAs, и он должен заменить часть кода в обещании success выше.

Ответ 2

Вот как вы это делаете:

  • Забудьте об IE8/IE9, это не стоит усилий и не выплачивает деньги.
  • Вам нужно использовать правильный HTTP-заголовок, используйте Accept для 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', а также вам нужно поместить responseType в 'arraybuffer' (ArrayBuffer, но с нижним регистром).
  • Сохранение HTML5 используется для сохранения фактических данных в желаемом формате. Обратите внимание, что это все равно будет работать без добавления типа в этом случае.
$http({
    url: 'your/webservice',
    method: 'POST',
    responseType: 'arraybuffer',
    data: json, //this is your json data string
    headers: {
        'Content-type': 'application/json',
        'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    }
}).success(function(data){
    var blob = new Blob([data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx');
}).error(function(){
    //Some error log
});

Совет! Не смешивайте "и", "придерживайтесь всегда", в профессиональной среде вам нужно будет пройти проверку js, например, jshint, то же самое происходит с использованием === и not == и т.д., Но это еще одна тема:)

Я бы поставил save excel в другой сервис, так что у вас есть чистая структура, и почта находится в правильном сервисе. Я могу сделать скрипт JS для вас, если вы не получите мой пример работы. Тогда мне также понадобятся некоторые данные json от вас, которые вы используете для полного примера.

Счастливое кодирование.. Эдуардо

Ответ 3

Загрузите ответ сервера как буфер массива. Сохраните его как Blob, используя тип контента с сервера (который должен быть application/vnd.openxmlformats-officedocument.spreadsheetml.sheet):

var httpPromise = this.$http.post(server, postData, { responseType: 'arraybuffer' });
httpPromise.then(response => this.save(new Blob([response.data],
    { type: response.headers('Content-Type') }), fileName));

Сохраните blob на пользовательском устройстве:

save(blob, fileName) {
    if (window.navigator.msSaveOrOpenBlob) { // For IE:
        navigator.msSaveBlob(blob, fileName);
    } else { // For other browsers:
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
        window.URL.revokeObjectURL(link.href);
    }
}

Ответ 4

Невозможно (насколько мне известно) запускать окно загрузки в вашем браузере из Javascript. Единственный способ сделать это - перенаправить браузер на URL-адрес, который передает файл в браузер.

Если вы можете изменить свой сервис REST, вы можете решить его, изменив так, чтобы POST-запрос не отвечал двоичным файлом, но с URL-адресом этого файла. Это даст вам URL-адрес в Javascript вместо двоичных данных, и вы можете перенаправить браузер на этот URL-адрес, который должен запросить загрузку, не выходя из исходной страницы.

Ответ 5

Работал для меня -

$scope.downloadFile = function () {
        Resource.downloadFile().then(function (response) {
            var blob = new Blob([response.data], { type: "application/pdf" });
            var objectUrl = URL.createObjectURL(blob);
            window.open(objectUrl);
        },
        function (error) {
            debugger;
        });
    };

Что вызывает следующее из моего ресурса factory -

  downloadFile: function () {
           var downloadRequst = {
                method: 'GET',
                url: 'http://localhost/api/downloadFile?fileId=dfckn4niudsifdh.pdf',
                headers: {
                    'Content-Type': "application/pdf",
                    'Accept': "application/pdf"
                },
                responseType: 'arraybuffer'
            }

            return $http(downloadRequst);
        }

Убедитесь, что ваш API также задает тип содержимого заголовка -

        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");

Ответ 6

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

//////////////////////////////////////////////////////////
//Server side 
//////////////////////////////////////////////////////////
imports ***
public class AgentExcelBuilder extends AbstractExcelView {

protected void buildExcelDocument(Map<String, Object> model,
            HSSFWorkbook workbook, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        //poi code goes here ....

        response.setHeader("Cache-Control","must-revalidate");
        response.setHeader("Pragma", "public");
        response.setHeader("Content-Transfer-Encoding","binary");
        response.setHeader("Content-disposition", "attachment; filename=test.xls");

        OutputStream output = response.getOutputStream();

        workbook.write(output);
        System.out.println(workbook.getActiveSheetIndex());
        System.out.println(workbook.getNumberOfSheets());
        System.out.println(workbook.getNumberOfNames());
        output.flush();
        output.close(); 
}//method buildExcelDocument ENDS

//service.js at angular JS code
function getAgentInfoExcel(workgroup,callback){
        $http({
            url: CONTEXT_PATH+'/rest/getADInfoExcel',
            method: "POST",
            data: workgroup, //this is your json data string
            headers: {
               'Content-type': 'application/json'
            },
            responseType: 'arraybuffer'
        }).success(function (data, status, headers, config) {
            var blob = new Blob([data], {type: "application/vnd.ms-excel"});
            var objectUrl = URL.createObjectURL(blob);
            window.open(objectUrl);
        }).error(function (data, status, headers, config) {
            console.log('Failed to download Excel')
        });
    }
////////////////////////////////in .html 

<div class="form-group">`enter code here`
                                <a href="javascript:void(0)" class="fa fa-file-excel-o"
                                    ng-click="exportToExcel();"> Agent Export</a>
                            </div>

Ответ 7

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

Просто разместите невидимую форму на своей странице.

<form name="downloadForm" action="/MyApp/MyFiles/Download" method="post" target="_self">
    <input type="hidden" name="value1" value="{{ctrl.value1}}" />
    <input type="hidden" name="value2" value="{{ctrl.value2}}" />
</form>

И поместите этот код в свой контроллер angular.

ctrl.value1 = 'some value 1';  
ctrl.value2 = 'some value 2';  
$timeout(function () {
    $window.document.forms['downloadForm'].submit();
});

Этот код отправит ваши данные в MyApp/MyFiles/Download и вы получите файл в папке "Загрузки".
Работает с Internet Explorer 10.

Если обычная форма HTML не позволяет вам размещать сложный объект, у вас есть два варианта:


1. Стрингируйте свой объект и поместите его в одно из полей формы в виде строки.

<input type="hidden" name="myObjJson" value="{{ctrl.myObj | json:0}}" />


2. Рассмотрим формы HTML JSON: https://www.w3.org/TR/html-json-forms/

Ответ 8

Я создал службу, которая сделает это за вас.

Перейдите в стандартный объект $http и добавьте дополнительные параметры.

1) Параметр "тип". Указание типа файла, который вы извлекаете. По умолчанию: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
2) Параметр "fileName". Это необходимо и должно включать расширение.

Пример:

httpDownloader({
  method : 'POST',
  url : '--- enter the url that returns a file here ---',
  data : ifYouHaveDataEnterItHere,
  type : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // this is the default
  fileName : 'YourFileName.xlsx'
}).then(res => {}).catch(e => {});

Это все, что вам нужно. Файл будет загружен на пользовательское устройство без всплывающего окна.

Здесь git repo: https://github.com/stephengardner/ngHttpDownloader

Ответ 9

Я столкнулся с этой же проблемой. Позвольте мне рассказать вам, как я решил это и достиг всего того, что вам все кажется желающим.

Требования:

  • Должна быть кнопка (или ссылка) к файлу - (или сгенерированный поток памяти)
  • Необходимо нажать кнопку и загрузить файл

В моей службе (я использую Asp.net Web API) у меня есть контроллер, возвращающий "HttpResponseMessage". Я добавляю "StreamContent" в поле response.Content, устанавливаю заголовки в "application/octet-stream" и добавляю данные в виде вложения. Я даже даю ему имя "myAwesomeFile.xlsx"

response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(memStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "myAwesomeFile.xlsx" };

Теперь вот трюк;)

Я сохраняю базовый URL-адрес в текстовом файле, который я прочитал в переменной в значении Angular, называемом apiRoot. Я делаю это, объявляя его, а затем устанавливая его на функцию "запуска" модуля, например:

app.value('apiRoot', { url: '' });
app.run(function ($http, apiRoot) {
    $http.get('/api.txt').success(function (data) {
        apiRoot.url = data;
    });
});

Таким образом, я могу установить URL-адрес в текстовом файле на сервере и не беспокоиться о том, чтобы "выдуть его" в загрузке. (Вы всегда можете изменить его позже по соображениям безопасности, но это отвлекает от развития;))

И ТЕПЕРЬ волшебство:

Все, что я делаю, это создание ссылки с URL-адресом, который напрямую попадает на мою конечную точку обслуживания и нацелен на "_blank".

<a ng-href="{{vm.getFileHref(FileId)}}" target="_blank" class="btn btn-default">&nbsp;Excel File</a>

секретный соус - это функция, которая устанавливает href. Вы готовы к этому?

vm.getFileHref = function (Id) {
    return apiRoot.url + "/datafiles/excel/" + Id;
}

Да, это.;)

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

Надеюсь, это поможет!