AngularJS $http-post-конвертировать двоичный файл в excel и скачать


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

в приведенном ниже коде я передаю информацию в виде JSON и отправляю ее на сервер REST web service (java) через angular $http post. Веб-служба использует информацию из JSON и создает книгу Excel. В ответе в теле успеха $http post я получаю двоичные данные в этом сведения переменной, но не знаю как чтобы преобразовать его и загрузить в виде файла 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) {

    });
9 51

9 ответов:

просто заметил, что вы не можете использовать его из-за 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
});

есть некоторые проблемы с этим хотя бы:

  1. он не поддерживает IE 8 и 9:
  2. он открывает всплывающее окно, чтобы открыть objectUrl какие люди могли бы заблокирован
  3. генерирует странные имена файлов

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

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

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

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

  1. забудьте IE8 / IE9, это не стоит усилий и не возвращает деньги.
  2. вам нужно использовать правильный заголовок HTTP, используйте Accept to ' application / vnd.openxmlformats-officedocument.spreadsheetml.sheet ' а также вам нужно поставить responseType в 'arraybuffer'(ArrayBuffer но установлен в нижнем регистре).
  3. HTML5 saveAs используется для сохранения фактических данных в нужный формат. Обратите внимание, он все равно будет работать без добавления типа в этом случай.
$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, то же самое касается использования === и не ==, и так далее, но это другая тема :)

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

удачи в кодировании.. Эдуардо

загрузите ответ сервера в виде буфера массива. Храните его как большой двоичный объект, используя тип контента с сервера (который должен быть 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);
    }
}

работал на меня -

$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;
        });
    };

который вызывает следующее из моей фабрики ресурсов -

  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");

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

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

Ответ № 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>

вы также можете использовать альтернативный подход-вам не нужно использовать $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>

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

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/

Я создал сервис, который будет делать это за вас.

пройти в стандартный $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 РЕПО:https://github.com/stephengardner/ngHttpDownloader

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

требования:

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

в моем обслуживании, (я использую Asp.net Web API), у меня есть контроллер, возвращающий "HttpResponseMessage". Я добавляю "StreamContent" в ответ.Содержание поле, установите заголовки в "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 в текстовом файле, который я читаю в переменную в угловом значении под названием "apiRoot". Я делаю это, объявляя его, а затем устанавливая его на функцию " run " модуля, например:

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-адрес конечной точки службы, которая доставляет файл.

надеюсь, что это помогает!