Good afternoon.
I can't get the browser to download the file from the server. I took the code from a previous project and it doesn't work. Please explain why.
On the server, files are collected in a zip archive. It is necessary to download the archive. I am using this:
My controller
#SneakyThrows
#GetMapping("/report/UploadDocuments")
#ResponseBody
public ResponseEntity<InputStreamResource> uploadDocuments(HttpServletResponse response,
#RequestParam("check") String check){
deleteAllFilesFolder(Directories.DYRECTORY_EXPORT);
ArrayList<Long> idDocuments = Converter.arrayStringInLong(check);
List<PaymentOrderArchive> documents = paymentOrderArchiveService.findAllById(idDocuments);
for (PaymentOrderArchive p : documents){
UploadingFiles.dowloadFileInDirectory(p);
}
//"method" create zip-file and return path
String s = method(documents);
//Problem here
UploadingFiles up = new UploadingFiles();
return up.downloadFile1(servletContext);
}
My method for dowload
public ResponseEntity<InputStreamResource> downloadFile1(ServletContext servletContext) throws IOException {
MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(servletContext, Directories.NAME_ZIP);
File file = new File(Directories.DYRECTORY_EXPORT + Directories.NAME_ZIP);
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
// Content-Disposition
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + Directories.NAME_ZIP)
// Content-Type
.contentType(mediaType)
// Contet-Length
.body(resource);
}
public class MediaTypeUtils {
public static MediaType getMediaTypeForFileName(ServletContext servletContext, String
fileName) {
String mineType = servletContext.getMimeType(fileName);
try {
MediaType mediaType = MediaType.parseMediaType(mineType);
return mediaType;
} catch (Exception e) {
return MediaType.APPLICATION_OCTET_STREAM;
}
}
Everything is working. The program does not give errors. But the file is not downloading. And I can't understand why.
Related
We have a RestController with the below endpoint
#PostMapping(path = "/downloadFile", produces = MediaType.MULTIPART_FORM_DATA_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public FileDownloadResponse downloadFile(#RequestBody FileDownloadRequest request) {
FileDownloadResponse downloadResponse = new FileDownloadResponse();
File file = new File("c:/fileLocation/"+request.getFileName());
try (InputStream stream = new FileInputStream(file)) {
byte[] bytes = IOUtil.toByteArray(stream);
downloadResponse.setFileName(file.getName());
downloadResponse.setCheckSum(calculateCheckSum(bytes));
downloadResponse.setFileContents(new FileSystemResource(bytes, file.getName()));
} catch (Exception e) {
e.printStackTrace();
}
return downloadResponse;
}
public class FileDownloadResponse {
private String fileName;
private Long checkSum;
private Resource fileContents;
}
public static class FileSystemResource extends ByteArrayResource {
private String fileName;
public FileSystemResource(byte[] byteArray , String filename) {
super(byteArray);
this.fileName = filename;
}
public String getFilename() { return fileName; }
public void setFilename(String fileName) { this.fileName= fileName; }
}
And on the Client Side we have the below code,
public class FileDownloadResponseClient {
private String fileName;
private Long checkSum;
private MultipartFile fileContents;
}
public FileDownloadResponseClient download(FileDownloadRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(Mediatype.ALL));
HttpEntity<FileDownloadRequest> requestEntity = new HttpEntity<>(request, headers);
return restTemplate.postForEntity(downloadUrl, requestEntity, FileDownloadResponseClient.class);
}
When we run the Rest Client above, we are getting the below error,
org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : [no body]
Is it possible to download a multipartfile along with other additional fields? If yes, what is that we are missing here, please let us know.
Thanks in Advance!
org.springframework.web.multipart has a method boolean isEmpty() to find if the file has no content. Best put that check there and redirect to a message about such a file multipart form.
Of [no body] i have found that message on test requests to http server but in entirety generally means there is nothing in the form or no extra information needed for the server to complete the request.
For now i presume the spring framework handles all the url decoding and boundary marker stripping (on both ends) of uploaded files.
I want to serve a .yaml file via a REST endpoint with Spring, I know that it cannot be directly displayed in a browser (just talking about Chrome here), since it doesn't support display of yaml files.
I have included what I think is the necessary library for this purpose
compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.9.9'.
If I open the endpoint /v2/api-doc in the browser, it will prompt me, to download a file named exactly as the endpoint /v2/api-doc. It contains the correct content.
Question: Is there a way to correctly transfer the .yaml file, so that the user will be prompted to safe myfile.yaml?
#RequestMapping(value = "/v2/api-doc", produces = "application/x-yaml")
public ResponseEntity<String> produceApiDoc() throws IOException {
byte[] fileBytes;
try (InputStream in = getClass().getResourceAsStream("/restAPI/myfile.yaml")) {
fileBytes = IOUtils.toByteArray(in);
}
if (fileBytes != null) {
String data = new String(fileBytes, StandardCharsets.UTF_8);
return new ResponseEntity<>(data, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
You should set a Content-Disposition header (and I recommend using ResourceLoader to load resources in Spring Framework).
Example:
#RestController
public class ApiDocResource {
private final ResourceLoader resourceLoader;
public ApiDocResource(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
#GetMapping(value = "/v2/api-doc", produces = "application/x-yaml")
public ResponseEntity produceApiDoc() throws IOException {
Resource resource = resourceLoader.getResource("classpath:/restAPI/myfile.yaml");
if (resource.exists()) {
return ResponseEntity
.ok()
.contentType(MediaType.parseMediaType("application/x-yaml"))
.header("Content-Disposition", "attachment; filename=myfile.yaml")
.body(new InputStreamResource(resource.getInputStream()));
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
I have a controller which returns file stream using ResponseEntity class.
But I'm not sure if the resource is closed after finished the method.
#RequestMapping(value = "/VMS-49001/playlist/{listName:.+}")
#ResponseBody
public ResponseEntity<?> playlist(HttpServletRequest request, HttpServletResponse response,
#PathVariable String listName) throws IOException {
String hlsPath = getHLSPath(request.getParameter("dt"), listName, OtuEnum.URLType.HLS);
Path filePath = Paths.get(hlsPath);
if (filePath.toFile().exists()) {
Path fileNamePath = filePath.getFileName();
String fileName = "";
if (fileNamePath != null) {
fileName = fileNamePath.toString();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData(fileName, fileName);
return ResponseEntity.ok().contentLength(filePath.toFile().length())
.contentType(MediaType.parseMediaType("application/vnd.apple.mpegurl")).headers(headers)
.body(new InputStreamResource(Files.newInputStream(filePath)));
} else {
String errorMsg = "404 file not found";
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.parseMediaType("text/html"))
.body(errorMsg);
}
}
if you see below code fragment, Files.newInputStream(filePath) implements Closeable, so it should be closed after use but I can't find the code closing it. :
return ResponseEntity.ok()
.contentLength(filePath.toFile().length())
.contentType(MediaType.parseMediaType("application/vnd.apple.mpegurl"))
.headers(headers)
.body(new InputStreamResource(Files.newInputStream(filePath)));
To response file stream, is it good to serve the file with this code? Or is there any better approach?
With Spring 4.1 your approach will work there is no issue in it.
Here below is another approach in case if you want to look :
#RequestMapping(value = "/VMS-49001/playlist/{listName:.+}")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/vnd.apple.mpegurl"));
headers.setContentDispositionFormData(fileName, fileName);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
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
I'm new using CXF and Spring to make RESTful webservices.
This is my problem: I want to create a service that produces "any" kind of file(can be image,document,txt or even pdf), and also a XML. So far I got this code:
#Path("/download/")
#GET
#Produces({"application/*"})
public CustomXML getFile() throws Exception;
I don't know exactly where to begin so please be patient.
EDIT:
Complete code of Bryant Luk(thanks!)
#Path("/download/")
#GET
public javax.ws.rs.core.Response getFile() throws Exception {
if (/* want the pdf file */) {
File file = new File("...");
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition", "attachment; filename =" + file.getName())
.build();
}
/* default to xml file */
return Response.ok(new FileInputStream("custom.xml")).type("application/xml").build();
}
If it will return any file, you might want to make your method more "generic" and return a javax.ws.rs.core.Response which you can set the Content-Type header programmatically:
#Path("/download/")
#GET
public javax.ws.rs.core.Response getFile() throws Exception {
if (/* want the pdf file */) {
return Response.ok(new File(/*...*/)).type("application/pdf").build();
}
/* default to xml file */
return Response.ok(new FileInputStream("custom.xml")).type("application/xml").build();
}
We also use CXF and Spring, and this is my preferable API.
import javax.ws.rs.core.Context;
#Path("/")
public interface ContentService
{
#GET
#Path("/download/")
#Produces(MediaType.WILDCARD)
InputStream getFile() throws Exception;
}
#Component
public class ContentServiceImpl implements ContentService
{
#Context
private MessageContext context;
#Override
public InputStream getFile() throws Exception
{
File f;
String contentType;
if (/* want the pdf file */) {
f = new File("...pdf");
contentType = MediaType.APPLICATION_PDF_VALUE;
} else { /* default to xml file */
f = new File("custom.xml");
contentType = MediaType.APPLICATION_XML_VALUE;
}
context.getHttpServletResponse().setContentType(contentType);
context.getHttpServletResponse().setHeader("Content-Disposition", "attachment; filename=" + f.getName());
return new FileInputStream(f);
}
}