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.
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 => {
...
}
)
I'm having an issue where I am trying to download a simple "text/plain" file in a spring controller method. I'm getting the text that I exactly want in the web tools response when running the app, which is "test". The response headers in the web developer tools are as follows:
Content-disposition: attachment; filename=file.txt
Content-type: text/plain
Content-length: 4
Length is 4 since that's the number of bytes that the text "test" is. In the controller, I have produces = MediaType.TEXT_PLAIN_VALUE. When I click the associated button in the application to download the file, however, rather than showing the download in the web browser, the download is made to the disk because the file.txt actually shows up in my project's workspace in intellij (which I'm using for my IDE). So, my question is how do I get the download to occur in the web browser, meaning what happens when you click on the 'Download Source Code' button at the following link https://howtodoinjava.com/spring-mvc/spring-mvc-download-file-controller-example/, rather than the file downloading to my workspace/disk?
The support methods/classes look like the following:
public class TextFileExporter implements FileExporter {
#Override
public Path export(String content, String filename) {
Path filepath = Paths.get(filename);
Path exportedFilePath = Files.write(filepath, content.getBytes(),
StandardOpenOption.CREATE);
}
}
public interface FileExporter {
public Path export(String content, String filename);
}
The controller at hand is the following:
#GetMapping(value="downloadFile")
public void downloadFile(HttpServletRequest request, HttpServletResponse response) {
String filename = "example.txt";
String content = "test";
Path exportedpath = fileExporter.export(content, filename);
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
Files.copy(exportedpath, response.getOutputStream());
response.getOutputStream.flush();
}
Try using directly the Response entity to return an InputStreamResource
#RequestMapping("/downloadFile")
public ResponseEntity<InputStreamResource> downloadFile() throws FileNotFoundException {
String filename = "example.txt";
String content = "test";
Path exportedpath = fileExporter.export(content, filename);
// Download file with InputStreamResource
File exportedFile = exportedPath.toFile();
FileInputStream fileInputStream = new FileInputStream(exportedFile);
InputStreamResource inputStreamResource = new InputStreamResource(fileInputStream);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName)
.contentType(MediaType.TEXT_PLAIN)
.contentLength(exportedFile.length())
.body(inputStreamResource);
}
As #chrylis -cautiouslyoptimistic said try avoiding using low level objects, let it handle it by Spring itself
I need to create a rest service in java which will in turn connect to another rest service for file download. For now, I just need to transfer the file from the other backend to client but in future some processing/transformations would be done.
For all the web services in my project, we are using spring rest (for providing as well as consuming the services).
My question is what would be the appropriate way of doing it considering that the files would be large and I don't want to run into OutOfMemory errors.
People in some other posts have suggested to use streams on both the ends but is that really possible? For this, do I need to write the file on disk first?
My current code for file download (consumer) -
public BackendResponse<byte[]> callBackendForFile(BackendRequest request) {
String body = null;
ResponseEntity<byte[]> responseEntity = null;
URI uri = createURI(request);
MultiValueMap<String, String> requestHeaders = getHeadersInfo(request.getHttpRequest());
if (HttpMethod.GET.equals(request.getMethod())) {
responseEntity = restTemplate.exchange(uri, request.getMethod(),
new HttpEntity<String>(body, requestHeaders), byte[].class);
} else {
LOG.error("Method:{} not supported yet", request.getMethod());
}
BackendResponse<byte[]> response = new BackendResponse<>();
response.setResponse(responseEntity);
return response;
}
My client code (provider):
#RequestMapping(value = "/file", method = RequestMethod.GET, produces = "application/xml")
#ResponseBody
public void downloadFileWithoutSpring(HttpMethod method, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
BackendRequest request = new BackendRequest(method,
httpRequest.getRequestURI(), httpRequest.getQueryString(), httpRequest);
BackendResponse<byte[]> backendResponse = dutyplanService.getFile(request);
ResponseEntity<byte[]> response = backendResponse.getResponse();
httpResponse.addHeader("Content-Disposition", "attachment; filename=\"" + "attachment.zip" + "\"");
httpResponse.getOutputStream().write(response.getBody());
httpResponse.flushBuffer();
}
Note: The code above doesn't work somehow as the attachment downloaded is a corrupt file
I don't think you will need to create that file on server as long as you are having the bytearray content of it received from another server.
You can try changing value of produces annotation to the value application/zip (or application/octet-stream, depending on the target browser) instead of 'application/xml'
you can pass HttpServletResponse#getOutputStream() directly in restTemplate and write it without save file in server.
public void getFile(HttpServletResponse response) throws IOException {
restTemplate.execute(
"http://ip:port/temp.csv",
HttpMethod.GET,
null,
clientHttpResponse -> {
StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
return null;
}
);
}
note that after call getFile(), you should close outputStream like this
response.getOutputStream().close()
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
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!