With spring-boot 2.0.4 on the back-end and vue 2.5.16 / axios 0.18.0 on the front-end, my goal is to upload PDF files to the back-end database and retrieve them from the front-end.
I initially took inspiration from this example for the spring part:
And this gist for the Axios part: https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
Below is a snippet of my code:
To upload a file in Vue using axios (the 'this.file' variable holds the file selected via an "input type='file'" form input, AxiosService() sets the base URL and includes an Authorization header with a token):
createRecord() { let formData = new FormData(); formData.append("file", this.file); AxiosService() .post("/commands/material/", formData, { headers: { "Content-Type": "multipart/form-data" } }) .then(response => { console.log("File uploaded"); }) .catch(error => { console.log(error); });
The section in the spring part handling the upload looks as follows. In my Entity, the content field is defined as a byte[] annotated with @Lob.
@BasePathAwareController @RequestMapping("/commands/material") public class MaterialCommandHandler { @Autowired MaterialRepository MaterialRepository; @RequestMapping(method=POST, path = "/") public ResponseEntity create(@RequestParam("file") MultipartFile file){ MaterialEntity material = new MaterialEntity(); material.setName(file.getOriginalFilename()); material.setMimetype(file.getContentType()); try { material.setContent(file.getBytes()); } catch (IOException e) { e.printStackTrace(); } try { MaterialRepository.save(material); } catch (Exception e) { if (e instanceof DataIntegrityViolationException) { throw new InvalidCommandException("Data is invalid for creation."); } throw(e); } return ResponseEntity.status(HttpStatus.CREATED).body(material.getId()); }
Using this code, the entry is successfully created in the DB, with the content field in MySQL being of type longblob.
The method designed to return the content of the file:
@RequestMapping(method = GET, path = "/download/{fileId}") public ResponseEntity<byte[]> getFile(@PathVariable Long fileId) { Optional<MaterialEntity> fileOptional = materialRepository.findById(fileId); if(fileOptional.isPresent()){ FrancaisMaterialEntity file = fileOptional.get(); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachement; filename=\"" + file.getName() + "\""); return ResponseEntity.ok() .headers(headers) .body(file.getContent()); } return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); }
Lastly, the GET method sent from the front-end using axios:
downloadFile() { AxiosService() .get(`/commands/material/download/${this.material.id}`, { responseType: "blob", }) .then(response => { console.log(response); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement("a"); link.href = url; link.setAttribute("download", "CalculRanking.pdf"); document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); }) .catch(error => { console.log(error); this.errorMessage = error.response.data.message; }); }
When attempting to download the file, the browser pop-up appears correctly but unfortunately, the downloaded .pdf file seems to be broken with Chrome stating: "Error Failed to load PDF document," and I cannot open it in Preview either.
I suspect the issue may be due to incorrect content interpretation at some point during the process. Despite trying various solutions like adding the "Accept" header with value "application/pdf" and setting "responseType: arrayBuffer" in the get request, none have worked so far. Therefore, I am reaching out for assistance here. Thank you in advance for your help.