I am using Jhipster.
I am using docx4j to create a .docx file.
I want to download this .docx file from server to client.
But The file I download is corrupted.
On server side:
I generate my file and put it in a byte[]
WordprocessingMLPackage p = null;
...
File f = new File(filePath);
p.save(f);
byte[] stream = Files.readAllBytes(f.toPath());
I have tried to send it to the client in different format:
byte[]
byte[] encoded Base64
String
String encoded Base64
An example of what's look like my method:
// send back as String encoded in Base64
public ResponseEntity<FileDTO> getFile(#PathVariable Long id) throws URISyntaxException, IOException {
FileDTO result = fillRepository.findOne(id);
byte[] stream = FileUtil.getFile(id) // retrieve file as byte[]
byte[] encoded = Base64.encodeBase64(stream);
String encodedString = new String(encoded, "UTF-8");
result.setFile(encodedString);
return ResponseUtil.wrapOrNotFound(Optional.ofNullable(result));
}
On client side:
I retrieve my file as byte[] or String and I put it in a blob to be downloaded.
FileService.get({id: id}, function(result) {
var res = result.file;
// var res = Base64.decode(result.file);
vm.blob = new Blob([res], {type: 'data:attachment;charset=utf-8;application/vnd.openxmlformats-officedocument.wordprocessingml.document'});
vm.url = (window.URL || window.webkitURL).createObjectURL(vm.blob);
});
My service is declared like this:
(function() {
'use strict';
angular
.module('myApp')
.factory('FileService', FileService);
FileService.$inject = ['$resource', 'DateUtils'];
function FileService($resource, DateUtils) {
var resourceUrl = 'api/file/:id/generate';
return $resource(resourceUrl, {}, {
'get': {
method: 'GET',
responseType:'arraybuffer'
}});}})();
When I download the file word say:
"We're sorry. We can't open file.docx because we found a problem with its content."
And when I compare my original file and the one downloaded in notepad++ for example I see that binary content is not exactly the same like there was encode/decode issues...
Also the size are not the same:
Original file 13Ko
Downloaded file 18Ko
Could you help me on knowing how and why the file downloaded is corrupted.
I finally found a solution:
I directly send back the binary without convertion in the response.
And access it with a window.location
I a new Rest Controller without annotation:#RequestMapping("/api")
#RestController
public class FileGenerationResource {
...
#GetMapping("/file/{id}")
#Timed
public void getFile(#PathVariable Long id, HttpServletResponse response) throws URISyntaxException, IOException {
FileInputStream stream = fileService.getFile(id);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-disposition", "attachment; filename=test.docx");
IOUtils.copy(stream,response.getOutputStream());
stream.close();
}
}
The controller content:
(function() {
'use strict';
angular
.module('myApp')
.controller('MyController', MyController);
MyController.$inject = ['$timeout', '$scope', '$stateParams', '$uibModalInstance'];
function MyController ($timeout, $scope, $stateParams, $uibModalInstance) {
var vm = this;
vm.clear = clear;
vm.dwl = dwl;
function dwl (id) {
window.location = "http://localhost:8080/file/"+id;
vm.clear();
}
function clear () {
$uibModalInstance.dismiss('cancel');
}
}
})();
Related
Hello everyone i am using angularjs java and rest to implement one report. Based on UI field selected there is a call to Java Layer and from java there is some database call and the returned input stream i am downloading in a csv file.
There is one problem happening if i do the same with hitting the the same url by browser which i m passing through angularjs than i m able to download the file but if by using UI i m making the request than there is no download option and data is returned as a stream in http response to angular.
java code:
enter code here
#Path("/files")
public class DownloadCsvFile {
#GET
#Path("/csv")
#Produces({MediaType.APPLICATION_OCTET_STREAM})
public Response getFile() {
StreamingOutput outp = new StreamingOutput() {
#Override
public void write(OutputStream out) throws IOException,
WebApplicationException {
String url ="http://someurl?
indent=on&q=RCE_POST:2016&sort=id%20asc
&rows=100000&start=0&wt=csv";
final InputStreamReader is = new InputStreamReader(
((HttpURLConnection) (new URL(url)).openConnection())
.getInputStream(),
Charset.forName("UTF-8"));
IOUtils.copy(is, out);
}
};
ResponseBuilder response = Response.ok(outp);
response.header("Content-Disposition", "attachment;
filename=\"testFile_file.csv\"");
return response.build();
} }
AngularJs controller code :
enter code here
var app = angular.module('myApp', ['ngProgress']);
app.controller('myCtrl', function($scope,$http,ngProgressFactory) {
// on submit the fun is called
$scope.LMALLPeriodReport =function()
{
return $http.get("http://localhost:8080/IsaveIdeas/rest/files/csv?
parameters="+parameter)
//parameter contain the selected field in UI
.then(function (response) {
var result = response.data;
alert("printing data");
});
};
The same request from the browser http://localhost:8080/IsaveIdeas/rest/files/csv? parameters={parameter} enable me to download the file.
You can use Blob in your angularjs code like this:
....
.then(function (response) {
var fileName = "yourFileName.csv";
var a = document.createElement("a");
document.body.appendChild(a);
response.data = "\ufeff" + response.data;
var file = new Blob([response.data], {encoding:"UTF-8",type:'application/csv;charset=UTF-8'});
var fileURL = URL.createObjectURL(file);
a.href = fileURL;
a.download = fileName;
a.click();
}
Am uploading a file (any format doc, docx, pdf, text, etc) as multipart form/data to a REST API from Postman or application UI. The text file uploads fine. All other non-text formats get corrupted. I cant open those files.
The size of the uploaded file increases drastically. Check the following server log:
File size just before call to request.getRequestDispatcher().forward(): 27583
Controller, first line in method:39439
The size of the uploaded file is 27.3Kb
I am guessing the files gets corrupted because of the other data appended to the file.
Controller method is
#RequestMapping(value="/entity/{entity}/{entityId}/type/{type}/{derive}",method = RequestMethod.POST)
#ResponseBody
public String uploadFile(#RequestParam("file") MultipartFile multipartFile,#PathVariable("entity")String entity,#PathVariable("type")String type,#PathVariable("entityId")Long entityId,#PathVariable("derive") boolean derive) throws Exception
Since text file is saving correctly and other files also get written correctly, don't think the code to write the file is incorrect.
Code to get inputStream
public String storeFile(MultipartFile multipartFile, String entity, Long id, String uploadType, boolean isDerive,String customName)
throws Exception
{
try
{
System.out.println(multipartFile.getSize());
String fileName = "";
String contentType = "";
if (multipartFile != null)
{
fileName = multipartFile.getOriginalFilename();
contentType = multipartFile.getContentType();
if (contentType == null)
{
contentType = "application/msword";
}
}
InputStream is = multipartFile.getInputStream();
String filePath = getFileName(entity, uploadType, id, fileName, isDerive,customName);
Helper.storeFile(is, filePath);
precedingPath = precedingPath.length() > 0 ? precedingPath + "/":"";
return precedingPath + filePath;
}
catch (WebException e)
{
e.printStackTrace();
throw e;
}
catch (Exception e)
{
e.printStackTrace();
throw new WebException(e.getMessage(), IHttpConstants.INTERNAL_SERVER_ERROR, e);
}
}
Helper.storeFile
public static File storeFile(InputStream is, String filePath) throws IOException {
try {
String staticRepoPath = null;
if (MasterData.getInstance().getSettingsMap().containsKey(Settings.REPO_LOCATION.toString())) {
staticRepoPath = MasterData.getInstance().getSettingsMap().get(Settings.REPO_LOCATION.toString());
} else {
throw new WebException("Invalid Settings");
}
byte[] buffer = new byte[is.available()];
is.read(buffer);
File targetFile = new File(staticRepoPath + File.separator + filePath);
#SuppressWarnings("resource")
OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
return targetFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
My Ajax request is as follows
var fd = new FormData();
//Take the first selected file
fd.append("file", document.actualFile);
//Generic AJAX call
CandidateService.ajax_uploadDocumentWithDocType($scope.candidate.id, fd, document.docType, function (error, json)
Content type while uploading:
var config = {headers:{'X-Auth-Token':authToken, 'Content-Type': undefined}, transformRequest: angular.identity};
Would anyone know how I can fix this and upload the file successfully?
Q1) Why does the file size change between the request dispatcher and the controller that handles the file data.
Q2) Could this change of file size be the cause of file corruption? Libre Office cause General Input/Output Error.
I figured the problem with the file upload. I had a spring filter in between that was changing the request to a wrappedRequest. This was adding additional data to the multipart data and causing the file to be corrupted.
Well in my case I had this exact same problem when accesing the API through Amazon API Gateway. Turned out I forgot to allow multipart ContentType on API Gateway.
Kind of weird the requests still made it to my server and text files worked fine.
My problem is that I am getting the wrong sized file on the client side. Here is my #Controller ...
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity<?> download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
try {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
ResponseEntity<InputStreamResource> result;
return ResponseEntity.ok()
.contentLength(Files.size(zippath))
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(new FileInputStream(zippath.toFile())));
} catch (Exception ex) {
// ErrorInfo is another class, unimportant
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorInfo(ex));
}
}
... and here is my client-side code using angular-file-saver ...
$http({url: "export/download/" + exportitem.exportId, withCredentials: true})
.then(function(response) {
function str2bytes(str) {
var bytes = new Uint8Array(str.length);
for (var i=0; i<str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
var blob = new Blob([str2bytes(response.data)], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, "download.zip");
}, $exceptionHandler);
The original file is 935673 bytes but response.data is 900728 and passing it through the transformation to Uint8Array results in a Blob that is 900728 in size as well. Either way, the resulting saved file is 900728 bytes (34945 bytes shy). Also it is not quite the same in what gets written. It seems to slightly get bloated but then the last part just seems to be truncated. Any ideas what I might be doing wrong?
UPDATE
I just updated my controller method to be the following and got the exact same result. Grrr.
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public void download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=download.zip");
InputStream inputStream = new FileInputStream(zippath.toFile());
org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
So the problem turned out to be angular's $http service. I also tried jQuery's ajax method. Both gave the same result. If I instead use the native XMLHttpRequest it works correctly. So the Java code was sound. I first verified this by exposing the file directly to the internet and then both using curl and directly accessing in the browser I managed to download the file of the correct size. Then I found this solution so that I could also download the file via javascript.
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
if (xhr.readyState === 4) {
var blob = xhr.response;
FileSaver.saveAs(blob, filename);
}
};
xhr.send();
Why does angular or jQuery give the wrong result? I still don't know but if anyone wishes to give an answer that uses those it would be appreciated.
responseType: blob
did the trick for a zip file
Angular 2 +
this.http.get('http://localhost:8080/export', { responseType: ResponseContentType.Blob })
.subscribe((res: any) => {
const blob = new Blob([res._body], { type: 'application/zip' });
saveAs(blob, "fileName.zip");
i just stumbled over the 'responseType' in $http requests, you are probably looking for 'blob': https://docs.angularjs.org/api/ng/service/$http#usage
By following this link PLUNKER . I want to show pdf file in new window, but I want to read the pdf file from server
My service code
#RequestMapping(value = "/retrievePDFFile", method = RequestMethod.GET)
public #ResponseBody
InputStream retrievePDFFile() throws FileNotFoundException
{
InputStream inputStream = new FileInputStream("/resources/AngularJS 2013.pdf");
return inputStream;
}
My angular controller
$http({
method : "GET",
url : "/service/retrievePDFFile"
}).success(function(data) {
console.log(data);
}).error(function(data, status) {
console.log(data);
});
I got the pdf input stream from server like this..
How to read this, and open as a PDF file in new tab or window..
Thanks
After lot of searching I achieved the goal by little bit change in my controller code
$http.get('/retrievePDFFiles', {responseType: 'arraybuffer'})
.success(function (data) {
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
});
in my application, users can edit an ODF file via WebODF (http://webodf.org/). On save, i want to send the edited file to a servlet, have it convert to PDF via ODFDOM (http://code.google.com/p/xdocreport/wiki/ODFDOMConverterPDFViaIText) and open in a new window.
Currently i am trying to do this via AJAX. Everything works fine up to the point where i try to open the received PDF file.
My Javascript:
function showPDF(pServletUrl)
{
var successCallback = function(pData)
{
var mimetype = "application/vnd.oasis.opendocument.text";
var blob = new Blob([pData.buffer], {type: mimetype});
var formData = new FormData();
formData.append("file", blob, "test.odt");
jQuery.ajax({
type: "POST",
url: pServletUrl,
async: false,
data: formData,
processData: false,
contentType: false,
success: function(pSuccessData)
{
window.open(pSuccessData);
},
error: function(pErrorData)
{
console.log(pErrorData);
}
});
}
var errorCallback = function(data)
{
console.log(error);
}
_canvas.odfContainer().createByteArray(successCallback, errorCallback);
}
My servlet:
public void handleRequest(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException
{
BufferedInputStream tBufferedInput = null;
BufferedOutputStream tBufferedOutput = null;
try
{
List<FileItem> tItems = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(pRequest);
for (FileItem tItem : tItems)
{
if (!tItem.isFormField())
{
String tFieldname = tItem.getFieldName();
String tFilename = FilenameUtils.getName(tItem.getName());
InputStream tFilecontent = tItem.getInputStream();
if("file".equals(tFieldname))
{
tBufferedInput = new BufferedInputStream(tFilecontent);
pResponse.reset();
pResponse.setHeader("Content-Type", "application/pdf");
pResponse.setHeader("Content-Disposition", "inline; filename=\"" + "test.pdf" + "\"");
tBufferedOutput = new BufferedOutputStream(pResponse.getOutputStream(), 10240);
this.getOdtAsPdf(tBufferedInput, tBufferedOutput);
tBufferedOutput.flush();
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
try
{
tBufferedInput.close();
tBufferedOutput.close();
}
catch(Exception e)
{
}
}
}
private void getOdtAsPdf(InputStream pInputStream, OutputStream pOutputStream) throws Exception
{
OdfDocument tOdfDocument = OdfDocument.loadDocument(pInputStream);
PdfOptions tPdfOptions = PdfOptions.create();
PdfConverter.getInstance().convert(tOdfDocument, pOutputStream, tPdfOptions);
}
It seems like Javascript wants to parse the recieved PDF file as a URL and (obviously) fails doing so. Is there a way to just open the file in a new window or do i have to find another way to do this?
You can't open the file using Ajax. This is a security restriction fo javascript. You have a few workarounds:
use a plugin which gives a Ajax type experience but opens a file in a new window.more details here
have a form which is submitted to a new window. <form target=_blank /> this will cause a new window to open thus not changing the contents of your current page.
Another option (not so neat) is to store the file in session and in the response of your AJAX, pass the id. Then using Javascript make a call using window.open('downloadurl?id') which will send the response of your PDF file.
You can make use an embed tag to display your blob after you make an ajax call.
Use createObjectUrl method to get url from blob and then display your pdf.