I have my REST response set up like so:
#MethodMapping(value = "/download", httpMethod = HttpMethod.GET)
public Response getLogFile() {
File log = new File("path");
if (log.exists())
{
return Response.ok(log, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + log.getName() + "\"" ) //optional
.build();
}
}
But the link just gives me a text response rather than a download:
{"status":200,"entity":{"path":"path"},"metadata":{"Content-Type":["application/octet-stream"],"Content-Disposition":["attachment; filename\u003d\"proteus.log\""]},"entityClosed":false,"entityBufferred":false}
The two packages I'm using are javax.ws.rs.core.Response and javax.ws.rs.core.MediaType.
Am I understanding the functionality wrong? Thanks!
Related
we have S3 storage ,there are a lot of some files, jpg,mp3 and others
what i need to do?
i need to redirect client to get the file from our s3 without uploading it on our server
and i want that clien get the file on his pc with name and extension
so it looks like clien send us uuid - we find link of this file on s3 and redirect it like this
#GetMapping("/test/{uuid}")
public ResponseEntity<Void> getFile(#PathVariable UUID uuid) {
var url = storageServiceS3.getUrl(uuid);
try {
var name = storageServiceS3.getName(uuid);
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
.header(HttpHeaders.LOCATION, url)
.header(HttpHeaders.CONTENT_TYPE, new MimetypesFileTypeMap().getContentType(name))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + name)
.build();
} catch (NoSuchKeyException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.build();
}
}
everything works good ,the file is downloading but one problem - the file has no name (its name still is key from s3) and no extension.
i think this code not works correctly
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + name)
is there any way to do this or i still need upload file to server and then send it to client ?
Finally i found solution- i use S3Presigner ,make presigned url and redirect it with simple Http response
#Bean
public S3Presigner getS3Presigner() {
return S3Presigner.builder()
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)))
.region(Region.of(REGION))
.endpointOverride(URI.create(END_POINT))
.build();
}
public String getPresignedURL(UUID uuid) {
var name = getName(uuid);
var contentDisposition = "attachment;filename=" + name;
var contentType = new MimetypesFileTypeMap().getContentType(name);
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(BUCKET)
.key(uuid.toString())
.responseContentDisposition(contentDisposition)
.responseContentType(contentType)
.build();
GetObjectPresignRequest getObjectPresignRequest =
GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(5))
.getObjectRequest(getObjectRequest)
.build();
PresignedGetObjectRequest presignedGetObjectRequest =
s3Presigner.presignGetObject(getObjectPresignRequest);
return presignedGetObjectRequest.url().toString();
}
#GetMapping("/redirect/{uuid}")
public void redirectToS3(#PathVariable UUID uuid, HttpServletResponse response) {
try {
var URI = storageServiceS3.getPresignedURL(uuid);
response.sendRedirect(URI);
} catch (NoSuchKeyException | IOException e) {
response.setStatus(404);
}
}
It works pretty good ;)
#Алексеев станислав
Some work arround for this is consuming your rest service by javascript and add file's name in a new header response and rename file when download by client.
// don't forget to allow X-File-Name header on CORS in spring
headers.add("X-File-Name", nameToBeDownloaded );
Example on ANGULAR but can be parsed to other language
this.http.get(uri_link_spring_redirecting_to_S3, {
responseType: 'blob',
observe: 'response'
}).subscribe(
(response) => {
var link = document.createElement('a');
var file = new Blob([response.body], {
type: 'text/csv;charset=utf-8;'
});
link.href = window.URL.createObjectURL(file);
link.download = response?.headers?.get('X-File-Name');; 'download.csv';
link.click();
},
error => {
...
}
)
Just started leaning Spring Reactive, and I couldn't find any examples on how to do something as simple as this. Without WebFlux, My controller was looking like this:
#GetMapping("/retrieveAttachment/{id}")
public ResponseEntity<byte[]> downloadFile(#PathVariable String id) throws Exception {
Attachment attachment = documentManagerService.getAttachment(id);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + attachment.getFileName() + "\"")
.body(attachment.getFileAttachment().getData());
}
Now with WebFlux, my documenManagerService returning Mono<Attachment>, this is what I came up with:
public Mono<ServerResponse> getAttachment(ServerRequest request) {
Mono<byte[]> mono = documentManagerService.getAttachment(request.pathVariable("id"))
.map(attachment -> attachment.getFileAttachment().getData());
return ServerResponse
.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM.toString())
.body(mono, byte[].class);
}
I'm able to download the file, but the problem is the file is without extension
since I've not set the Content Disposition Header. How do I set it without making a blocking call?
By using flatMap maybe. Im writing this on mobile so have not tried it out or double checked anything.
public Mono<ServerResponse> getAttachment(ServerRequest request) {
return documentManagerService.getAttachment(request.pathVariable("id"))
.flatMap(attachment -> {
return ServerResponse.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM.toString())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + attachment.getFileName() + "\"")
.bodyValue(attachment.getFileAttachment().getData())
});
}
I could see many related topics, but I have a specific problem. I am using spring boot controller to download a zip file. I am able to download the file when it is http verb get, but as I have to pass a big json payload I changed to post. Since then instead of downloading it as file it is responding the contents of the file with some ascii characters. Below is the method in controller for downloading the file.
#ApiResponses(value = { #ApiResponse(code = 404, message = "file could not be found"),
#ApiResponse(code = 200, message = "File was created sucessfully") })
#PostMapping(path="/download-file/1.0", produces="application/zip")
public ResponseEntity<InputStreamResource> downloadFile(
#ApiParam(value = "File creation contents", required = true) #RequestBody InputDetailsVO inputDetailsVO) {
File file = null;
InputStreamResource resource = null;
HttpHeaders headers = new HttpHeaders();
try {
//Creating InputStreamResource out of zip file
resource = new InputStreamResource(new FileInputStream(file));
String contentType = "application/zip";
if (!StringUtils.isEmpty(contentType)) {
headers.setContentType(MediaType.parseMediaType(contentType));
}
headers.add("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
} catch (Exception e) {
log.error("Issue with file creation",e);
}
return ResponseEntity.ok()
.contentLength(file.length())
.contentType(MediaType
.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.headers(headers).body(resource);
}
Below is the response I am getting instead of file download
PK;��N <?xml version="1.0" encoding="UTF-8"?>
<employeeDetails>
<name>Harry</name>
<age>30</30>
<email>test#test.com</test>
</employeeDetails>PK�qB�#Y;YPK;��N�qB�#Y;Yemployee details.xmlPKL�Y
Try like this, you can download any type of file. I assume that InputDetailsVO contains the name of the file or you can have your own logic to pick the file name. On the top of this method, you can provide swagger related annotations.
#PostMapping(value = "/download-file/1.0", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> downloadFile(#RequestBody InputDetailsVO inputDetailsVO) {
String dirPath = "your-location-path";
byte[] fileBytes = null;
try {
String fileName = inputDetailsVO.getFileName();
fileBytes = Files.readAllBytes(Paths.get(dirPath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(fileBytes);
}
I also had a similar use case. I am sharing the code which had solved the issue.
#RequestMapping(value="/download",method=RequestMethod.GET,produces="application/zip" )
public ResponseEntity<?> download(HttpServletResponse response) throws IOException
{
//Some Code...
File file = new File("F:\\Folder\\Folder\\Folder\\"+filename);
InputStreamResource resource2 = new InputStreamResource(new FileInputStream(file));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", String.format("inline; filename=\"" + filename + "\""));
response.setHeader("responseType", "arraybuffer");
response.setHeader("Content-Length", ""+file.length());
return new ResponseEntity<InputStreamResource>(resource2,HttpStatus.ACCEPTED);
}
I use angularjs 1.3.14
I have a java REST services produce a xml file with this header: "Content-Disposition", "attachment; filename=yourFileName";
I need take the file with the name of my file on AngularJS.
I have this code:
$http.get('/someUrl').success(function(data, status, headers){
var myHeaders = headers();
...
});
but in myHeaders have only {content-type="application/xml"}. I need find "Content-Disposition", "attachment; filename=yourFileName"
Java Services:
#GET
#Path(EXPORT_URL)
#Produces(MediaType.APPLICATION_XML)
public Response export(#Context HttpServletRequest request) {
String userName = request.getRemoteUser();
if (userName != null) {
...
ResponseBuilder response = Response.ok(myObject);
response.header("Content-Disposition", "attachment; filename=myFile.xml");
return response.build();
} else {
return Response.status(Status.FORBIDDEN).build();
}
}
2 years after, I find a solution:
#RequestMapping(value = "/export", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML)
public ResponseEntity<String> export(...
HttpHeaders headers = new HttpHeaders();
headers.setAccessControlExposeHeaders(Collections.singletonList("Content-Disposition"));
headers.set("Content-Disposition", "attachment; filename=" + filename);
return new ResponseEntity<>(exportedContent, headers, HttpStatus.OK);
This is a server side CORS issue. You need to enable this:
"Access-Control-Expose-Headers", "Content-Disposition"
I have a web service developed using Eclipse. Now I want to test it using RESTClient program. I want the client to download the video, which I have defined like this in Eclipse:
#Path("/university")
public class Video {
//this is the location of the .avi
private static final String VIDEO_FILE = "F:\\file.avi";
#GET
#Path("/video")
#Produces("video/avi")
public Response getVideoFile() {
File file = new File(VIDEO_FILE);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename=\"abc.avi\"");
return response.build();
}
#GET
#Path("/{fileName}/video")
#Produces("video/avi")
public Response getFileInVideoFormat(#PathParam("fileName") String fileName) {
System.out.println("File requested is : " + fileName);
if (fileName == null || fileName.isEmpty()) {
ResponseBuilder response = Response.status(Status.BAD_REQUEST);
return response.build();
}
File file = new File("c:/abc.avi");
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename=abc.avi");
return response.build();
}
}
but I am getting errors when I test using RESTClient (where I specify METHOD=GET, HEADER(key=accept,value=video/avi)). What might the problem?
First, if your REST service does not #Consume something, you don't have to specify "Accept" parameter in header (see HTTP request specification).
Second, your call should look like contextpath/university/video or contextpath/university/filename/video.
Third, don't use ambiguous REST paths like /video and /{anystring}/video, your application server can understand your calls wrong. Use patterns (for example /{id:[0-9]+) or rename /{filename}/video to /video/{filename} to avoid ambiguousness.