I am a beginner programmer, help with the implementation of uploading a text file via rest-api java.
I have already implemented the simplest action - unload a file from the server, here is my code:
#GetMapping(value = "/file/{filename:.+}")
public ResponseEntity<Resource> unloadFile(#PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
I can test the file unload by simply following the link!
I cannot test the upload. I find it difficult to write tests. Please tell me if I got a working code and maybe there is a better way to upload. My code upload:
#PostMapping(value = "/file")
public ResponseEntity<MultipartFile> uploadFile(MultipartFile file) {
storageService.store(file);
return ResponseEntity.ok().body(file);
}
Thank you so much!
To upload the file/files using spring boot application we use same method that we had for servlet containers. In your controller
#PostMapping("/uploadFile")
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = yourStorageService.storeFile(file);
String = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
//You can even generate download links.
return new ResponseEntity<Object>(HttpStatus.Ok, "Uploaded", fileDownloadUri);
}
To download the files:
#GetMapping("/downloadFile/{fileName}")
public ResponseEntity<Resource> downloadFile(#PathVariable String fileName, HttpServletRequest request) {
// Load file as Resource from DB or local
Resource resource = fileStorageService.loadFileAsResource(fileName);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
For #PostMapping(value = "/file") endpoint , its best to return a success/error status instead of returning the file,if file is larger ..it takes time to return back.
Better to return success state. 200 ok.
Related
I have an unusual task. I have to download the file and immediately force my browser to save it locally to disk?
Below, what I have written so far
#PostMapping("/uploadFile")
public String uploadFile(#RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes, Model model) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("errorMessage", "No file to upload.");
return "index";
}
if(getExtension(file.getOriginalFilename()).equals("XLS") || getExtension(file.getOriginalFilename()).equals("XLSX")) {
Path copyLocation = Paths
.get(UPLOAD_DIR + File.separator + StringUtils.cleanPath(file.getName()));
try {
Files.copy(file.getInputStream(), copyLocation, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
redirectAttributes.addFlashAttribute("successMessage", "File upload successfully, uploaded file name: " + file.getOriginalFilename());
}
return "response";
}
Well great, but you could show what you already have and write exactly what you have a problem with. Can't set the content disposition? Fine, but then you could show what you already have and write exactly what you have a problem with. Can't set the content disposition?
#PostMapping(value = "/", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> handleFileUpload(#RequestParam("file") MultipartFile file) {
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=dupa.xlsx").body(file.getResource());
}
Instead of returning String (view in this case I assume) you can change your endpoint to return byte array with the file content
public #ResponseBody byte[] uploadFile(
You should also provide configuration to the #PostMapping with the media type of the file if you know what kind it's going to be.
#PostMapping(value = "/uploadFile", produces = MediaType.IMAGE_JPEG_VALUE)
Another solution is to return ResponseEntity. The documentation has two examples for it (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html)
I have a small problem which I've been unable to solve for a few hours. I am basically trying to stream an excel file from the resources folder withing a jar. The file has around 9KB in my file manager, however, when I download it by visiting the REST endpoint I receive a 13/14KB file which can no longer be opened by excel. The metadata is set correctly, as is the filename. I suspect the streaming/copying process is somehow corrupting the file. Here you may see the code snippet:
public void getTemplateByDataType(HttpServletResponse response, DataType dataType) {
String fileName = "excel_template.xlsx";
String templateDirectory = "templates";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
InputStream data = this.getClass().getClassLoader().getResourceAsStream(templateDirectory + "/" + fileName); // loading file from resources folder
try {
IOUtils.copy(data, response.getOutputStream()); // copying to httpservletresponse output stream
} catch (IOException e) {
//...
}
}
I've already tried reading from a simple text file in the same location in order to verify whether the getResourceAsStream call works and this is the case. So I am somehow breaking something with the IOUtils.copy I guess? Does anybody have any suggestions why this simple code snippet breaks my xlsx files?
Just to get the full picture, the controller is relatively simple:
#GetMapping(value = "/templates", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public #ResponseBody
void getFileTemplate(HttpServletResponse response,
#ApiParam(value = "Type of data import", required = true) #RequestParam String dataType) {
importService.getTemplateByDataType(response, DataType.fromValue(dataType));
}
Try Streaming Output. Maybe this would help you Example of using StreamingOutput as Response entity in Jersey
If you want to download as an attachment, then return the Response like below:
Response.status(Response.Status.OK)
.header(HttpHeaders.CONTENT_DISPOSITION,
String.format("attachment; filename=\"download.gz\""))
.entity(streamingOutput)
.build();
StreamingOutput streams the content of the file and at the client end, it will be downloaded as an attachment.
try to copy this file directly to your output stream.
#GetMapping(value = "/templates", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public #ResponseBody
void getFileTemplate(HttpServletResponse response,
#ApiParam(value = "Type of data import", required = true) #RequestParam String dataType) {
String fileName = "excel_template.xlsx";
String templateDirectory = "templates";
Path templateFilePath = Paths.get(getClass().getClassLoader().getResource(templateDirectory + "/" + fileName).toURI());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
try {
FileCopyUtils.copy(new BufferedInputStream(new FileInputStream(templateFilePath.toFile())), response.getOutputStream());
response.getOutputStream().flush();
response.getOutputStream().close();
} catch (IOException e) {
//...
}
}
```
I am currently making a file upload within my spring boot project. I am using rest controller for my controller as that is what is written within the tutorial that I am using https://www.callicoder.com/spring-boot-file-upload-download-jpa-hibernate-mysql-database-example/
However, I found out that apparently rest controller unable to display HTML page based on what is written here: How to return a html page from a restful controller in spring boot?
Is there a way for displaying my HTML page while retaining the rest controller?
this is my controller
#RestController
public class RekonsiliasiController {
private static final Logger logger = LoggerFactory.getLogger(RekonsiliasiController.class);
#Autowired
private DBFileStorageService dbFileStorageService;
#RequestMapping("/rekonsiliasi")
public String index() {
System.err.println("MASUK PAK EKO OI");
return "rekonsiliasi";
}
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
DBFile dbFile = dbFileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(dbFile.getId())
.toUriString();
return new UploadFileResponse(dbFile.getFileName(), fileDownloadUri,
file.getContentType(), file.getSize());
}
#GetMapping("/downloadFile/{fileId}")
public ResponseEntity<ByteArrayResource> downloadFile(#PathVariable String fileId) {
// Load file from database
DBFile dbFile = dbFileStorageService.getFile(fileId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(dbFile.getFileType()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + dbFile.getFileName() + "\"")
.body(new ByteArrayResource(dbFile.getData()));
}
}
and this is my rekonsiliasi.html
<form method="POST" action="/uploadFile" enctype="multipart/form-data">
<input type="file" name="file" /><br/><br/>
<input type="submit" value="Submit" />
</form>
This is what I get currently, a blank page with a simple text
UPDATE
I tried to divide between the index and the file upload to the following.
RekonsiliasiController.java
#Controller
public class RekonsiliasiController {
#RequestMapping("/rekonsiliasi")
public String index() {
System.err.println("MASUK PAK EKO OI");
return "rekonsiliasi";
}
FileController.java
#RestController
public class FileController {
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
#Autowired
private DBFileStorageService dbFileStorageService;
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
System.err.println("CEK");
DBFile dbFile = dbFileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(dbFile.getId())
.toUriString();
return new UploadFileResponse(dbFile.getFileName(), fileDownloadUri,
file.getContentType(), file.getSize());
}
#GetMapping("/downloadFile/{fileId}")
public ResponseEntity<Resource> downloadFile(#PathVariable String fileId) {
// Load file from database
DBFile dbFile = dbFileStorageService.getFile(fileId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(dbFile.getFileType()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + dbFile.getFileName() + "\"")
.body(new ByteArrayResource(dbFile.getData()));
}
}
Now the HTML is showing perfectly fine but when I upload the file I got 403 Error. Before I tried to find the problem for this part, I'd like to know whether if there are some ways for displaying my HTML page while retaining the rest controller?
EDIT Delete the 'uploadMultipleFiles' method in FileController.Java and it still gets me 403 error
First of all, in your code in the controller method uploadMultipleFiles, you are actually calling the controller method uploadFile directly by treating it just like another method which is not ideal. The fact that you need to recursively call another controller endpoint means that it is a design flaw in itself. So , try to remove this bit of code by providing the logic in service layer to handle this scenario .
Secondly , splitting the view controller and the rest controller into two separate classes is the right approach. The #RestController annotation is designed to serve json response by default that is why we have the #Controller annotation which serves both model and view. The response code 403 that you receive has nothing to do with this.
I have the following code for a method thats supposed to return a file for download. The path variables are used to navigate to a specific file on the filesystem.
#GetMapping("/files/{username}/{docId}/{revisionNo}/{filename:.}")
#ResponseBody
public ResponseEntity<Resource> serveFile (#PathVariable("username") String username,
#PathVariable("docId") String docId,
#PathVariable("revisionNo") String revisionNo,
#PathVariable("filename") String filename)
{
System.out.println("Serve file firing"); // for debug
String filepath = username + "/" + docId + "/" + revisionNo + "/" + filename;
Resource file = storageService.loadAsResource(filepath); // this method works fine
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+file.getFilename()+"\"")
.body(file);
}
However when I start the server and go to "http://localhost:8080/files/admin/0/0/Capture1.PNG" I get a 404 error and no download. The debug println on line 9 doesn't print which would indicate this method is not being triggered.
For context this similar code does work correctly although it doesn't use the filesystem hierarchy i need and only returns files in the root folder.
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> serveFile(#PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+file.getFilename()+"\"")
.body(file);
}
I think then it must be to do with how I've used the path variables but all the research I've done indicates that its correct...running out of time to fix please help!
i want to make a rest api controller (spring boot) that when petitioned with a get will allow me to download an excel file. currently i have this endpoint:
#RequestMapping(value = "/download.xls", method = RequestMethod.GET)
public ResponseEntity Survey_Reports(#RequestParam(value = "evaluated") String evaluated){
return surveyService.getSurveysFile(evaluated);
}
wich ultimately calls to this method:
public static ResponseEntity getDownloadResponse() {
File file2Upload = new File("Survey_Reports.xls");
Path path = Paths.get(file2Upload.getAbsolutePath());
ByteArrayResource resource = null;
try {
resource = new ByteArrayResource(Files.readAllBytes(path));
} catch (IOException e) {
logger.error("there was an error getting the file bytes ", e);
}
return ResponseEntity.ok()
.contentLength(file2Upload.length())
//this line doesnt seem to work as i set the file format in the controller request mapping
.contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
.body(resource);
}
everything seems to work semi-fine as i get the download.xls(as the mapping) file correclty, but now i want to make the downloaded file have some specific name like: evaluatedName.xls or userDateEndDate.xls or some other stuff, is there a way to edit the response entity to do so? so that i dont have to name the mapping "download.xls"
In context of HttpServletResponse response you can do this like this
response.setContentType("application/csv");
response.setHeader("Content-Disposition", "attachment; filename=" + csvName);
and for ResponseEntity i assume you can use something like this:
ResponseEntity.ok().header("Content-Disposition","attachment; filename=" + csvName );