Rest service : issues to download multiple Zip files using : ResponseBuilder - java

I implemented a Rest Service to donwload files from a directory.
**There is my rest service : No problem here **
#GET
#Path("/download")
#Produces("application/zip")
public Response downloadZippedFile() {
File file = new
File("C:/Users/Desktop/Files/201707111513.zip");
ResponseBuilder responseBuilder = Response.ok((Object) file);
responseBuilder.header("Content-Disposition", "attachment;
filename=\"MyJerseyZipFile.zip\"");
return responseBuilder.build();
}
Résult ==> I can download 201707111513.zip from the browser.
-------------------------------------------------
Now, I am trying download multiple .zip files. it causes issues
There is my 2nd rest service : The main problem
#GET
#Path("/download")
#Produces("application/zip")
public Response downloadZippedFile() {
ArrayList<String> PdfInputListFiles = new ArrayList<>();
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111513.zip");
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111514.zip");
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111515.zip");
// to print every file in every PdfInputListFiles
for (String myFile : PdfInputListFiles) {
File file = new File(myFile);
ResponseBuilder responseBuilder = Response.ok((Object) file);
responseBuilder.header("Content-Disposition", "attachment; filename=\"MyJerseyZipFile.zip\"");
return responseBuilder.build();
}
return null;
}
Résult ==> I can ONLY download the first file 201707111513.zip.
And it's normale cause of the return responseBuilder.build() line in the end of For loop.
-------------------------------------------------
Now, I am tryin this :
#GET
#Path("/download")
#Produces("application/zip")
public Response downloadZippedFile() {
ArrayList<String> PdfInputListFiles = new ArrayList<>();
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111513.zip");
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111514.zip");
PdfInputListFiles.add("C:/Users/Desktop/Files/201707111515.zip");
// to print every file in every PdfInputListFiles
for (String myFile : PdfInputListFiles) {
getFile(myFile);
}
return null;
}
public Response getFile(String str) {
// Response response = null;
File file = new File(str);
ResponseBuilder responseBuilder = Response.ok((Object) file);
responseBuilder.header("Content-Disposition", "attachment; filename=\"MyJerseyZipFile.zip\"");
return responseBuilder.build();
}
Résult ==> No download is happen, I can't understand why calling getFile method doesn't return any downloaded file.
**
I need to download evry file in my list, in other words I have multiple paths and I need to download all those files.
SomeOne can help, or suggest an alternative solution !
Thank you

Related

How to copy/rename a file and return it without altering the existing file?

I have a generic.exe file which doesn't contain any users detail in it.
I also have a REST API which takes the userId and returns a File to the client.
Now, what we want to implement in our project is that when someone hits the REST API, we want to take that generic.exe and rename it to manager_userId.exe and return back this "manager_userId.exe".
Points to be noted over here is that:
The generic.exe file should not be modified/deleted at all
When 2 users (userA and userB) hit that same API simultaneously , they should get their own copy of manager_userA.exe and manager_userB.exe
The code what I have written is
#RequestMapping(value = "/downloadExecutable", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = {MediaType.APPLICATION_OCTET_STREAM})
#ResponseBody
public Response downloadExecutable(#RequestBody DownloadExecutableRequest downloadExecutableRequest,
HttpServletRequest request, HttpServletResponse response) {
File file = downloadExecutable(downloadExecutableRequest, request, response,
getUserID(request), osDetails);
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment;filename=" + file.getName()).build();
}
public File downloadExecutable(DownloadExecutableRequest downloadExecutableRequest, HttpServletRequest request,
HttpServletResponse response, String userId, String osDetails) {
File file = null;
String path = "/home/genericCopy/generic.exe";
synchronized (this) {
BufferedWriter fileWriter = null;
try {
File source = null;
source = new File(path);
Path destination = Paths.get("/tmp/");
Files.copy(source, destination.toFile());
fileWriter = new BufferedWriter(new FileWriter(destination.getFileName().toString()+"_"+userId));
file = new File(destination.getFileName().toString());
} catch (IOException e) {
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
return file;
}
The code is working , but it is creating a temporary file and then renaming it and then returning it back but it will keep on creating the copies of the file for each request.
Is there any smarter way that i can achieve not to create such temporary copies of the user specific files and also handle a scenario when 2 users hit the API simultaneously ?
The name of the file which is downloaded by user has no relationship to the name of the file on disk.
You can just specify any name of the file in the header and the user will see the name.
Specifically, you would just set the filename you want the user to see to the Content-Disposition header and always load the same exe file from the disk.
Something like this:
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment;filename=executable_" + getUserID(request) + ".exe";
You don't need to do any copying in the downloadExecutable function.
You don't need to create a copy of generic.exe file to return it with changed name. You can use correctly parametrised Content-Disposition header, so it would return same file every time, with file name provided by user.
Here you can find example:
#RestController
public class DemoController {
#GetMapping(value = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
#ResponseBody
public ResponseEntity downloadExecutable(#RequestParam("userId") String userId) throws IOException {
byte[] file = Files.readAllBytes(Paths.get("/home/genericCopy/generic.exe"));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_" + userId + ".exe")
.contentLength(file.length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(file);
}
}
and result of executing this method:

Download ODS/XLS from Rest API Java

I'm trying to make a download of a ODS file (Open Office's Calc file).
I'm using Java for back end and AngularJS for front end.
This is my code of the front end where I receive and download the file:
vm.downloadModel = () => {
DataFactory.GET(`${URL.CTM_ODS()}/rotas/modelo`, { headers: { 'Content-Type': 'application/octet-stream' } }).then(response => {
let file = new Blob([response], { type: 'application/vnd.oasis.opendocument.spreadsheet' });
let url = window.URL || window.webkitURL;
let downloadLink = angular.element('<a></a>');
downloadLink.attr('href', url.createObjectURL(file));
downloadLink.attr('target', '_self');
downloadLink.attr('download', 'Model_Routes_OD.ods');
downloadLink[0].click();
});
}
This is my endpoint on my back end:
#GetMapping(value = "/modelo")
#ResponseBody
public ResponseEntity<ByteArrayResource> getModelo() throws IOException {
String path = "C:\\Model_Routes_OD.ods";
File file = new File(path);
Path pathObj = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(pathObj));
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(Files.readAllBytes(pathObj).length));
headers.add(HttpHeaders.TRANSFER_ENCODING, "binary");
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
The file that is downloaded came corrupted! My file has 13Kb and when it downloads shows 21Kb, when I try to open he says that the file is "broken" and he can try to fix it but, without success.
I tried your code and api is working fine.
You don't need to explicitly handle file at frontend, instead you can just download it.
Example code for above scenario:
var app = angular.module('demo', []);
app.controller('Download', function($scope, $http) {
$scope.download = function(){
var url = "http://localhost:8080/modelo"
window.open(url, "_self");
}
});
I am able to download file perfectly and here is complete code for this example

file download via http post is returning the zip file contents

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

how to redirect rest service from post to get

I have written the code to redirect one rest service to another rest service .In SubmitId() method , i am redirecting another rest service( getLicense() and getError()) and i am unable to redirect it.Please help me out..(i am using JAXRS)
#Path("/resource")
public class DAMLicenseResource {
#POST
#Path("/submittree")
#Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
#Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
public Response SubmitId(ArrayList<String> selectedIds) throws JAXBException, IOException, URISyntaxException{
DAMLicenseService damLicenseService = new DAMLicenseService();
DAMLicenseOutput damLicenseOutput = damLicenseService.dupSubmittedId(selectedIds);
if(damLicenseOutput.isError()){
java.net.URI location1 = new java.net.URI("/DAMLicenseTool/damlicense/resource/error");
return Response.temporaryRedirect(location1).build();
}
else{
java.net.URI location1 = new java.net.URI("/DAMLicenseTool/damlicense/resource/download");
return Response.temporaryRedirect(location1).build();
}
}
#POST
#Path("/download")
#Produces("text/plain")
public Response getLicense(){
System.out.println("response api");
File file = new File("license.txt");
//write to this file
try {
System.out.println("response api");
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("license");
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
ResponseBuilder response = Response.ok((Object) file);
//System.out.println("response api");
response.header("Content-Disposition",
"attachment; filename=\"license.txt\"");
return response.build();
}
#POST
#Path("/error")
#Produces("text/plain")
public Response getErrorMsg(){
System.out.println("error");
return Response.status(200).entity("failed").build();
}
}
First of all Redirects are not right in REST, REST should not dictate the flow, try other ways to call GET from POST.
java.net.URI location = new java.net.URI("../download");
temporarily try this, see if it helps.
You don't need to give the complete path /DAMLicenseTool/damlicense/resource/error just give the URI starting from the Controller class Path
URI uri = new URI("/resource/error");
return Response.temporaryRedirect(uri).build();
return "redirect:/secondUrl";
or used Httpclient for call second URL

Web service call fails with RESTClient

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.

Categories