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 ответов:
просто заметил, что вы не можете использовать его из-за 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
какие люди могли бы заблокирован- генерирует странные имена файлов
это сработало!
Код на стороне сервера в 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
обещать выше.
вот как вы это делаете:
- забудьте IE8 / IE9, это не стоит усилий и не возвращает деньги.
- вам нужно использовать правильный заголовок HTTP, используйте Accept to ' application / vnd.openxmlformats-officedocument.spreadsheetml.sheet ' а также вам нужно поставить responseType в 'arraybuffer'(ArrayBuffer но установлен в нижнем регистре).
- 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
я столкнулся с этой же проблемой. Позвольте мне рассказать вам, как я решил ее и достиг всего, что вы все, кажется, хотите.
требования:
- должна быть кнопка (или ссылка) на файл - (или сгенерированный поток памяти)
- необходимо нажать кнопку и загрузить файл
в моем обслуживании, (я использую 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"> Excel File</a>
секретный соус-это функция, которая устанавливает href. Ты готов к этому?
vm.getFileHref = function (Id) { return apiRoot.url + "/datafiles/excel/" + Id; }
да, именно так. ;)
даже в такой ситуации если выполняется итерация по многим записям, содержащим файлы для загрузки, необходимо просто передать идентификатор функции, и функция создает url-адрес конечной точки службы, которая доставляет файл.
надеюсь, что это помогает!