I am trying to download csv file from the filepath that is saved in db. I have to read the csv file from filepath and download it when hitting the endpoint. Error I am getting says no converter for class BufferedReader with preset Content-Type 'application-csv'. I think the type I have with response entity BufferedReader is not taking my content-type as csv. I have seen resource type with response entity working fine. And I don't want csv to be generated, I already have it, i just have to read it and download it.
#GetMapping("/export")
public CompletableFuture <ResponseEntity<BufferedReader>> getCSVFile(#RequestParam Integer reportdt,
#RequestParam("conId") String conId) throws IOException {
CSVFilePath csvFilePath = csvFilePathRepository.findByConIdAndReportdt(conId, reportdt);
File file = new File(csvFilePath.getFilepath());
//FileInputStream csvfile = new FileInputStream(csvFilePath.getFilepath());
FileReader newfile = new FileReader(csvFilePath.getFilepath());
String fileName = file.getName();
if(file.exists()) {
return CompletableFuture
.completedFuture(ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName)
.contentType(MediaType.parseMediaType("application/csv"))
.body(new BufferedReader(newfile)));
} else {
return CompletableFuture
.completedFuture(ResponseEntity.status(HttpStatus.NOT_FOUND).body(new BufferedReader(newfile)));
}
}
Here is the error log.
error log
Related
I am trying to detect the File Extension of a file passed as an InputStream, the extension is detected correctly but the file tends to become corrupted after that. Here is my method for detecting Extension -
public static Optional<String> detectFileExtension(InputStream inputStream) {
// To provide mark/reset functionality to the stream required by Tika.
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
String extension = null;
try {
MimeTypes mimeRepository = getMimeRepository();
MediaType mediaType = mimeRepository.detect(bufferedInputStream, new Metadata());
MimeType mimeType = mimeRepository.forName(mediaType.toString());
extension = mimeType.getExtension();
log.info("File Extension detected: {}", extension);
// Need to reset input stream pos marker since it was updated while detecting the extension
inputStream.reset();
bufferedInputStream.close();
} catch (MimeTypeException | IOException ignored) {
log.error("Unable to detect extension of the file from the provided stream");
}
return Optional.ofNullable(extension);
}
private static MimeTypes getMimeRepository() {
TikaConfig config = TikaConfig.getDefaultConfig();
return config.getMimeRepository();
}
Now when I am trying to save this file after extension detection again using the same InputStream like -
byte[] documentContentByteArray = IOUtils.toByteArray(inputStream);
Optional<String> extension = FileTypeHelper.detectFileExtension(inputStream);
if (extension.isPresent()) {
fileName = fileName + extension.get();
} else {
log.warn("File: {} does not have a valid extension", fileName);
}
File file = new File("/tmp/" + fileName);
FileUtils.writeByteArrayToFile(file, documentContentByteArray);
It creates a file but a corrupted one. I guess after stream consumption in detectFileExtension the stream is not getting reset properly. If someone has done this before some guidance would be great, thanks in advance.
I fixed it by not using the same input stream again and again.
I created a new stream to pass for extension detection and the initial stream for creating the file.
byte[] documentContentByteArray = IOUtils.toByteArray(inputStream);
//extension detection
InputStream extensionDetectionInputStream = new ByteArrayInputStream(documentContentByteArray);
Optional<String> extension = FileTypeHelper.detectFileExtension(inputStream);
if (extension.isPresent()) {
fileName = fileName + extension.get();
} else {
log.warn("File: {} does not have a valid extension", fileName);
}
extensionDetectionInputStream.close();
//File creation
File file = new File("/tmp/" + fileName);
FileUtils.writeByteArrayToFile(file, documentContentByteArray);
If there is a better way to do that by reusing the same stream it would be great and I'll gladly accept that answer, for now, I am marking this as the accepted answer.
I am using the below method to download the file from server now for few files with extensions (".png",".txt",".pdf") the files are downloading correctly but where as for (".exe") the file is downloading as f.txt may i know what's wrong with this code.For .exe files the content is being written to a "f.txt" file . I am also usoing Files.probeContentType(file.toPath()); to determine the media type and setting it in content type of respnse entity.Thanks in advance! :).
the media type where extension changing is application/x-msdownload
public ResponseEntity<InputStreamResource> getFileFromDisk(String filename) throws IOException {
ResponseEntity<InputStreamResource> filedata = null;
String file_path = "/e:/filesfolder/" + filename;
String fileType = "Undetermined";
final File file = new File(file_path);
fileType = Files.probeContentType(file.toPath());
System.out.println(fileType);
InputStreamResource resource = new InputStreamResource(new FileInputStream(file_path));
String file_checksum = checksumcalc.calculateChecksum(file_path);
filedata = ResponseEntity.ok().header("Md5", file_checksum)
.contentType(MediaType.parseMediaType(fileType)).body(resource);
return filedata;
}
To avoid the f.txt issue, you have to define the Content-disposition header to specify the filename of the attachment.
For example :
filedata = ResponseEntity.ok()
.header("Content-disposition", "attachment; filename=" + fileName)
.header("Md5", file_checksum)
.contentType(MediaType.parseMediaType(fileType)).body(resource);
I have created a method which accepts the JSON response and export the data to Excel and CSV file. I want the method to accept the filename as a parameter, and return the File/FileHandle object from the method.
Can anyone help me regarding this?
I am using below code to export data from creteExcel and createCSV.
public void createExcel( PageModel pageModelRequest,PageModel pageModelResponse ){
FileOutputStream outputStream=new FileOutputStream(new File("GridData.xlsx"));
createGrid(pageModelResponse, sheet, workbook);
workbook.write(outputStream);
System.out.println("Excel Written");
outputStream.close();
workbook.close();
}
public void createCSV(PageModel pageModelRequest,PageModel pageModelResponse){
String csvFile = "/Users/abhinak4/Desktop/GridData.csv";
FileWriter writer = new FileWriter(csvFile);
createGrid(pageModelRequest, pageModelResponse, writer);
}
In the method createGrid, I am creating the workbook for excel, and I am appending the data, to the writer (writer.append).
I want to know, how can I accept the fileName as parameters for createExcel and createCSV method, and return the file with the same name containing the data?
I want the method to accept the filename as parameter : You can send a String, A File Object or InputStream - createExcel(PageModel req, PageModel resp, File yourFileObj) And return the File object from the method : The signature becomes: public File createExcel()
Use this way and you are able to get a file object
File file = new File(fileName);
FileOutputStream outputStream = new FileOutputStream(file);
workBook.write(outputStream);
My details explanation answer was deleted by someone so I explained here
I have a Spring controller with /file mapping that gets a file name from user and stream file content to user
#RequestMapping(value = "/file" , method = RequestMethod.GET)
#ResponseBody
public void getFile(#RequestParam(value = "name", required = true) String fileName,
HttpServletResponse response)
{
String fileExtension = "";
int i = fileName.lastIndexOf('.');
if (i > 0) {
fileExtension = fileName.substring(i+1);
}
// file extension for requested file must be xls
if(!fileExtension.equals("xls"))
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
try {
Path path = Paths.get("/tmp/" + fileName);
byte[] data = Files.readAllBytes(path);
response.setHeader("Content-Disposition", "inline; filename=" + fileName);
response.setContentType("application/vnd.ms-excel");
response.setContentLength(data.length);
try {
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(data);
outputStream.flush();
} catch (Exception e) {
}
} catch (Exception e) {
}
}
User only can download file with .xls extension in tmp folder. the problem with this code is that user can change directory and download other .xls files in other directories. for example if there is a file in this path /tmp/tmp2/ab.xls user can download the file with calling this url http://myserver.mydomain:myport/mycontext/file?name=tmp2/ab.xls that is a security hole. what is the best way for checking name that I give from user is a file name? (not directory/filename or ../filename or another dangerous path )
Path tmpPath = Paths.get("/tmp/"); //valid directory
String fileName = "foo/bar.xls"; //supplied fileName
Path filePath = tmpPath.resolve(fileName); //add fileName to path
Path fileParent = filePath.getParent(); //get parent directory
System.out.println(fileParent);
System.out.println(tmpPath.equals(fileParent)); //false because fileParent is '/tmp/foo'
'tmpPath' will be equals 'fileParent' if you supply a valid fileName like 'bar.xls'.
I think you can also simplify the extension checking: filePath.endsWith(".xls"); should be enough. And don't concatenate file paths ("/tmp/" + fileName). Paths.get("/tmp", fileName) will do that for you.
In this situation, I have created a zip file containing search result files, and am trying to send it to the user. Here is the chunk of code I am currently trying to use.
File[] zippable = new File[files.size()];
File resultFile = ZipCreator.zip(files.toArray(zippable), results);
InputStream result = new FileInputStream(resultFile);
IOUtils.copy(result, response.getOutputStream());
However, this currently doesn't work quite right. Instead of returning the zip file that I have created, it returns an html file. If I manually change the file extension afterwards, I can see that the contents of the file are still the search results that I need. So the problem just lies in returning the proper extension to the response.
Does anyone have any advice for this situation?
You need to set the Content-Type response header to the value application/zip (or application/octet-stream, depending on the target browser). Additionally, you may want to send additional response headers indicating attachment status and filename.
You need to set the content type header to application/octet-stream prior to streaming the results. Depends on what implementation of response you are using on how you actually do this.
Here is some working code, just in case anyone needs it:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// The zip file you want to download
File zipFile = new File(zipsResourcesPath + zipFileName);
response.setContentType("application/zip");
response.addHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.setContentLength((int) zipFile.length());
try {
FileInputStream fileInputStream = new FileInputStream(zipFile);
OutputStream responseOutputStream = response.getOutputStream();
int bytes;
while ((bytes = fileInputStream.read()) != -1) {
responseOutputStream.write(bytes);
}
} catch (IOException e) {
logger.error("Exception: " + e);
}
}
And the HTML:
<a class="btn" href="/path_to_servlet" target="_blank">Download zip</a>
Hope this helps!
So I found a hack for this : ) Just add ".zip" in your filename and set your content type as application/zip. Works like a charm.
response.setContentType("application/zip");
String licenseFileName = eId;
response.setHeader("Content-disposition", "attachment; filename=\"" + licenseFileName +".zip");