Download a pdf using ajax and spring mvc doesn't work - java

I'm trying to implement an ajax download. This is my code for the ajax request:
$('#download').click(function(){
$.ajax({
url: '${downloadPath}',
type: 'GET',
data: {${_csrf.parameterName}:'${_csrf.token}'},
success: function (res) {
}
});
});
And this is my controller's method:
#Secured("IS_AUTHENTICATED")
#RequestMapping(value="download/{id}", method=RequestMethod.GET, produces="application/pdf")
#ResponseBody
public void download(#PathVariable(value="id") final Long id, HttpServletResponse response) throws IOException {
CheckList checkList = checkListService.findById(id);
// byte[] byteItem = checkListService.getFileByIdDocument(id);
File f = new File(VariabiliGlobali.PATH_CHECKLIST+checkList.getPratica().getId()+"/"+id);
ServletOutputStream out = response.getOutputStream();
response.setContentType("application/pdf");
response.setContentLength((int)f.length());
response.setHeader("Content-Disposition", "attachment; filename=\"" + f.getName() + "\"");
FileInputStream in = new FileInputStream(f);
byte[] buffer = new byte[4096];
int length;
while( (length = in.read(buffer) ) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.flush();
}
I can see the pdf inside the response:
But my browser (Chrome) doens't do anything.
Where am I wrong? How can I dowload it?

You don't need ajax and you are using as content type application/octet-stream as we can see in your code here:
response.setContentType("application/octet-stream");
If you want to display the pdf inside the browser (if the browser has the proper plugin to read pdf) you should use:
the right pdf content type
set the proper header
In my code i did the following:
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "inline; filename=pdfFileName.pdf;");
In any case I'd suggest to yuo to use the "spring" way like this:
#Secured("IS_AUTHENTICATED")
#RequestMapping(value="download/{id}", method=RequestMethod.GET)
public ResponseEntity<InputStreamResource> download(#PathVariable(value="id") final Long id) throws IOException {
HttpHeaders respHeaders = new HttpHeaders();
MediaType mediaType = new MediaType("application","pdf");
respHeaders.setContentType(mediaType);
respHeaders.setContentDispositionFormData("inline", "pdfFileName.pdf");
//Here you have to take the InputStream of the file you want to download
InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);
}
I hope it's useful
Angelo

Set your content type in header. So browsers handles the pdf.
headers.setContentType(MediaType.parseMediaType("application/pdf"));
Below is the sample code.
#Secured("IS_AUTHENTICATED")
#RequestMapping(value="download/{id}", method=RequestMethod.GET, produces="application/pdf")
public ResponseEntity<?> download(#PathVariable(value="id") final Long id, HttpServletResponse response) throws IOException {
List<SampleDto> reportData = new ArrayList<SampleDto>();
HttpHeaders headers = new HttpHeaders();
if (null == reportData || reportData.size() == 0) {
return new ResponseEntity<byte[]>(null, headers, HttpStatus.NO_CONTENT);
}
byte[] contents = writePdfContentToBytes();//Here you should your code to get content in bytes.
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.setContentDispositionFormData("inline", "Report.pdf");
return new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
}

Related

How can I change PDF title in broswer view(Data from Spring MVC)

I want visit my program and get a pdf data show in broswer such like this:
but you can see the Upper left corner is my view path,not my filename, how can i change it?
the src is as follow:
#GetMapping("/getPDF")
public ResponseEntity<byte[]> getPDF() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
String filename = "测试.pdf";
File file = new File("E:\\2018版计信本科人才培养方案总2019.11.15(适用于2018级及以后年级)(1).pdf");
headers.add("content-disposition", "inline;filename=" + URLEncoder.encode(filename,"UTF-8"));
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);
return response;
}
done it!
you can use some tools like pdfbox and using stream to output
core src as follow:
File file = new File(pathPlace);
if (!file.exists()) {
throw new MyException(ResultEnum.FILE_EXIST);
}
String fileName = saveFileDirectoryMapper.getFileNameById(fileId);
response.setHeader("content-disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));
try (OutputStream out = response.getOutputStream();
// load pdf
PDDocument document = PDDocument.load(file)) {
// get document attribute
PDDocumentInformation info = document.getDocumentInformation();
// change title
info.setTitle(fileName);
document.setDocumentInformation(info);
// output data to stream
document.save(out);
}

StreamingResponseBody returning empty file

I'm trying to create a rest service to download files from a repository, using Springboot.
I'm trying to return a ResponseEntity with StreamingResponseBody, to return the file that i get from the repository, as an InputStream.
This is the current code i have:
#GetMapping(path = "/downloadFile")
public ResponseEntity<StreamingResponseBody> downloadFile(#RequestParam(value = "documentId") String documentId,
HttpServletRequest request, HttpServletResponse response) throws InterruptedException, IOException {
InputStream is = downloadService.getDocument(documentId);
StreamingResponseBody out = outputStream -> {
outputStream.write(IOUtils.toByteArray(is));
};
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/csv");
headers.add("Content-Disposition", "attachment; filename=" + documentId);
headers.add("Pragma", "no-cache");
headers.add("Cache-Control", "no-cache");
return (new ResponseEntity<>(out, headers, HttpStatus.OK));
}
When I consume this endpoint, using directly the browser, or postman, the file that is downloaded comes empty.
I understand that the OutputStream is written to asynchronously (Async is enabled in the config class).
How can I consume this service and get the file completely written, the way it comes from the repository I'm using ? ( if possible using Postman, just for testing purposes)
Am i building the service correctly?
I have modified the code bit little, in my documentId is the name of the file to be downloaded. I have tested, it is working fine. Check below the code.
#GetMapping(path = "/downloadFile")
public ResponseEntity<StreamingResponseBody> downloadFile(
#RequestParam(value = "documentId") String documentId,
HttpServletRequest request,
HttpServletResponse response)
throws InterruptedException, IOException {
String dirPath = "E:/sure-delete/"; //Directory having the files
InputStream inputStream = new FileInputStream(new File(dirPath + documentId));
final StreamingResponseBody out =
outputStream -> {
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes of file...");
outputStream.write(data, 0, nRead);
}
};
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/csv");
headers.add("Content-Disposition", "attachment; filename=" + documentId);
headers.add("Pragma", "no-cache");
headers.add("Cache-Control", "no-cache");
return ResponseEntity.ok().headers(headers).body(out);
}

File is being saved with Content-Disposition header

I have angular2 front-end and Dropwizard back-end. I'm trying to upload a picture from front-end to back-end.
My html code:
<input type="file" name="file" (change)="fileChange($event)">
My component:
fileChange(event) {
let fileList: FileList = event.target.files;
if (fileList.length > 0) {
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('file', file);
this.siteDescriptionService.sendPhoto(formData).subscribe(value => {
console.log("value", value);
});
}
}
My service:
sendPhoto(data): Observable<any> {
return this.http.postPhoto('api/site/savePhoto', data, null).map(res => res);
}
My http interceptor:
postPhoto(url: string, params?: any, options?: RequestOptionsArgs): Observable<any> {
this.beforeRequest();
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
let reqOptions = new RequestOptions({ headers: headers });
return super.post(this.getFullUrl(url), params, reqOptions)
.catch(this.onCatch)
.do((res: Response) => {
this.onSuccess(res);
}, (error: any) => {
this.onError(error);
})
.finally(() => {
this.onFinally();
});
}
The request is being send with such payload:
------WebKitFormBoundaryAz4AnN4lFPWKUvmH
Content-Disposition: form-data; name="file"; filename="logo.png"
Content-Type: image/png
------WebKitFormBoundaryAz4AnN4lFPWKUvmH--
And on my server I have:
#POST
#Timed
#Path("savePhoto")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response uploadFile(InputStream uploadedInputStream) throws IOException {
String uploadedFileLocation = "/tmp/photo1.png";
FormDataMultiPart part = new FormDataMultiPart().field("file", uploadedInputStream, MediaType.TEXT_PLAIN_TYPE);
FormDataBodyPart p = part.getField("file");
InputStream i = (InputStream) p.getEntity();
writeToFile( i, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
return Response.ok(output).build();
}
private void writeToFile(InputStream uploadedInputStream, String uploadedFileLocation)
throws IOException {
int read;
final int BUFFER_LENGTH = 1024;
final byte[] buffer = new byte[BUFFER_LENGTH];
OutputStream out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
out.flush();
out.close();
}
Everything is fine, file is being saved, but it's saved with the whole request payload, including Content-Disposition, Content-Type headers, etc., and therefore the file becomes "broken".
How can I remove the Content-Disposition header from file?
When you use an InputStream parameter, you're saying you want to whole request body. If you just want a single part, you need to annotate it with #FormDataParam with the name of the part
public Response uploadFile(#FormDataParam("file") InputStream file,
#FormDataParam("file") FormDataContentDisposition fdcd) {
String filename = fcdc.getFileName();
// You don't need to create the FormDataMultiPart
// just save the InputStream parameter
}
In order for this to work, you also need to to register the MutliPartFeature
env.jersey().register(MultiPartFeature.class);

Show pdf in new tab doesnt work with jquery and spring mvc

I want to show pdf in new tab after generated it, im using JQuery 1.12.3 and Spring MVC 4.
When i clic to my link, i generate pdf and my ajax method show success but my pdf isnt showed in new tab, what i've forgot ?
i've follewed this posts :
Display PDF in a webpage
Open ResponseEntity PDF in new browser tab
Spring - display PDF-file in browser instead of downloading
I've tried to remove ResponseBody annotation, but i've the same result
Here is my code :
HTML :
<a id="3676274" class="bulletinLink" target="_blank" href="#">Bulletin du 2015-04-30</a>
JQuery :
$(".bulletinLink").click(function(e){
e.preventDefault();
var id = $(this).attr('id');
var oAjax;
var sUrl = sUrlBase + '/generatePdf/'+id;
oAjax = $.ajax({
url: sUrl,
type: 'GET',
cache: false,
data : '',
async : false
});
oAjax.done(function(transport) {
alert("success");
});
oAjax.fail(function(transport) {
alert("fail");
});
});
Java :
#RequestMapping(value = "/generatePdf/{id}", method = RequestMethod.GET)
#ResponseBody
public final ResponseEntity<byte[]> generateWithResponseBody(#PathVariable("id") final int idBulletin
,final HttpServletRequest httpRequete, final HttpServletResponse httpReponse) throws ApplicationException
{
...
HttpHeaders headers = new HttpHeaders();
headers.setContentType( MediaType.parseMediaType( "application/pdf" ) );
String filename = "spring_tutorial.pdf";
headers.setContentDispositionFormData( filename, filename );
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(out.toByteArray(), headers, HttpStatus.OK);
return response;
}
The problem is you need to add the file to the HttpServletResponse, and no need to return any value in your method, so use void, adding the file to the response, the browser will handle it correctly
#RequestMapping(value = "/generatePdf/{id}", method = RequestMethod.GET)
#ResponseBody
public final void generateWithResponseBody(#PathVariable("id") final int idBulletin
,final HttpServletRequest httpRequete, final HttpServletResponse reponse) throws ApplicationException
{
//Here retrieve your PDF file
if(file != null) {
String mimeType = URLConnection.guessContentTypeFromName(file.getName());
if (mimeType == null) {
logger.debug("mimetype is not detectable, will take default");
mimeType = "application/pdf";
}
logger.debug("mimetype : {}", mimeType);
response.setContentType(mimeType);
response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));
response.setContentLength((int) file.length());
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
FileCopyUtils.copy(inputStream, response.getOutputStream());
}
}
I've change my jquery function without Ajax like this and it works know :
window.open(sUrl);
Also, for displaying my pdf in the tab and dont download it, i remove this instruction in controller :
headers.setContentDispositionFormData( filename, filename );

How to set 'Content-Disposition' and 'Filename' when using FileSystemResource to force a file download file?

What is the most appropriate, and standard, way to set the Content-Disposition=attachment and filename=xyz.zip using Spring 3 FileSystemResource?
The action looks like :
#ResponseBody
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET, produces = "application/zip")
#PreAuthorize("#authorizationService.authorizeMethod()")
public FileSystemResource doAction(#PathVariable String abcd, #PathVariable String efgh) {
File zipFile = service.getFile(abcd, efgh);
return new FileSystemResource(zipFile);
}
Although the file is a zip file so the browser always downloads the file, but I would like to explicitly mention the file as attachment, and also provide a filename that has nothing to do with the files actual name.
There might be workarounds for this problem, but I would like to know the proper Spring and FileSystemResource way to achieve this goal.
P.S. The file that is being used here is a temporary file, marked for deletion when the JVM exists.
In addition to the accepted answer, Spring has the class ContentDisposition specific for this purpose. I believe it deals with the file name sanitization.
ContentDisposition contentDisposition = ContentDisposition.builder("inline")
.filename("Filename")
.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(contentDisposition);
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET)
#PreAuthorize("#authorizationService.authorizeMethod(#id)")
public HttpEntity<byte[]> doAction(#PathVariable ObjectType obj, #PathVariable Date date, HttpServletResponse response) throws IOException {
ZipFileType zipFile = service.getFile(obj1.getId(), date);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
response.setHeader("Content-Disposition", "attachment; filename=" + zipFile.getFileName());
return new HttpEntity<byte[]>(zipFile.getByteArray(), headers);
}
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
#ResponseBody
public FileSystemResource getFile(#PathVariable("file_name") String fileName,HttpServletResponse response) {
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=somefile.pdf");
return new FileSystemResource(new File("file full path"));
}
Here is an alternative approach for Spring 4. Note that this example clearly does not use good practices regarding filesystem access, this is just to demonstrate how some properties can be set declaratively.
#RequestMapping(value = "/{resourceIdentifier}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
// #ResponseBody // Needed for #Controller but not for #RestController.
public ResponseEntity<InputStreamResource> download(#PathVariable(name = "resourceIdentifier") final String filename) throws Exception
{
final String resourceName = filename + ".dat";
final File iFile = new File("/some/folder", resourceName);
final long resourceLength = iFile.length();
final long lastModified = iFile.lastModified();
final InputStream resource = new FileInputStream(iFile);
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" + resourceName)
.contentLength(resourceLength)
.lastModified(lastModified)
.contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
.body(resource);
}
Made few changes to both given answers and I ended up with the best of both in my project where I needed to extract an image from the database as a blob and then serve it to the clients :
#GetMapping("/images/{imageId:.+}")
#ResponseBody
public ResponseEntity<FileSystemResource> serveFile(#PathVariable #Valid String imageId,HttpServletResponse response)
{
ImageEntity singleImageInfo=db.storage.StorageService.getImage(imageId);
if(singleImageInfo==null)
{
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
Blob image=singleImageInfo.getImage();
try {
String filename= UsersExtra.GenerateSession()+"xxyy"+singleImageInfo.getImage1Ext().trim();
byte [] array = image.getBytes( 1, ( int ) image.length() );
File file = File.createTempFile(UsersExtra.GenerateSession()+"xxyy", singleImageInfo.getImage1Ext().trim(), new File("."));
FileOutputStream out = new FileOutputStream( file );
out.write( array );
out.close();
FileSystemResource testing=new FileSystemResource(file);
String mimeType = "image/"+singleImageInfo.getImage1Ext().trim().toLowerCase().replace(".", "");
response.setContentType(mimeType);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", filename);
response.setHeader(headerKey, headerValue);
// return new FileSystemResource(file);
return ResponseEntity.status(HttpStatus.OK).body( new FileSystemResource(file));
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
Using a ResponseEntity in Kumar's code will help you respond with the correct Response code.
Note: converting from a blob to a file is quoted from this link:
Snippet to create a file from the contents of a blob in Java

Categories