I have problem with upload file to local WebDav. So far i have:
public interface IStorageService {
URI SaveFile(String filename, InputStream inputStream);
}
#Component
public class LocalStorageService implements IStorageService {
#Value( "C:\temp" )
private String filestorePath;
public URI SaveFile(String filename, InputStream inputStream) {
var rootLocation = Paths.get(filestorePath);
var filePath = rootLocation.resolve(filename);
try {
Files.copy(inputStream, filePath);
} catch (IOException e) {
throw new RuntimeException("Failure save file to " + filename + " in " + filestorePath + "." + e.getMessage(), e);
}
return filePath.toUri();
}
}
and controller
private final DocumentService documentService;
public DocumentController(DocumentService documentService) {
this.documentService = documentService;
}
#RequestMapping(method = RequestMethod.POST)
public DocumentModel handleFileUpload(#RequestParam("file") MultipartFile file) throws IOException {
return documentService.handleFileUpload(file.getOriginalFilename(), file.getInputStream());
}
And it is works correctly, the file is uploaded to C:/temp...
Now I would like to do the same but upload file to local WebDav. When i change in #Value "C:\temp to "http://localhost" (this is ma webdav location) i have:
invalidpathexception: illegal char <:> at index 4: http://localhost
or when I declare http//localhost without <:>
nosuchfileexception: http\localhost
How can I write my code to upload file directly to WebDav.
Parameters of SaveFile method cannot be changed, I need do it with Name as String and InputStream.
I tried with Sardine but to no avail. Could someone help me, give any tips or maybe suggestion of code ?
Greetings !
You can get the path where your web app/ war/ servlet / controllers are deployed :
ServletContext context = getContext();
String fullPath = context.getRealPath("/WEB-INF/test/foo.txt");
For a Spring project, in controller
'#Autowired
'ServletContext context;
And in controller method :
'String uploadPath = context.getRealPath("") + File.separator + UPLOAD_DIRECTORY;
And the real file name, but what if a user uploads same file twice or 2 users upload file with same name?
Better to put in sub directory with user id/ user name and maybe date time or some other identifier like TXN id + some fixed text like
' String fileName = context.getRealPath("") + File.separator + userId + readlName + "xyz."
extnFromMimeType;
And store the path in data base for this transaction/ user as per your busibess use case.
if mime type is image/PMG then extnFromMimeType will be "png"; if jpeg or jpg then "jpg"
See
File path to resource in our war/WEB-INF folder?
how to get getServletContext() in spring mvc Controller
In Spring MVC, how can I set the mime type header when using #ResponseBody
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
If can be many images per user/ transaction can also use createTemp File to get a unique file name
using a UUID also possible ... https://stackoverflow.com/a/1293712/1643558 or a large random number
Related
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.
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 using spring boot and I want to upload an image via a form.
It works, but there are 2 problems:
1) I do not know how to validate the file to make sure it is either jpg or png
2) I can upload only to the root folder, not to the static folder, I do not know how to set the correct path to the resources/static folder
#PostMapping("/add")
public String add(#Valid Product product, #RequestParam MultipartFile file, BindingResult bindingResult,
RedirectAttributes redirectAttributes, Model model) {
...
try {
byte[] bytes = file.getBytes(); // how to validate this?
Path path = Paths.get( file.getOriginalFilename()); // how to set path here?
Files.write(path, bytes);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded '" + file.getOriginalFilename() + "'");
} catch (IOException e) {
System.out.println("!!!!!!!!!!!!! Image uploaded problem !!!!!!!!!!!!!!");
e.printStackTrace();
}
...
}
To get file extension
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
Path
InputStream in = file.getInputStream();
File destination = new File("/some-location/" + file.getOriginalFilename());
FileUtils.copyInputStreamToFile(in, destination);
I'm trying to upload an image with spring MVC in src/main/webapp/resources to display it in an img tag (<img src="<c:url value="/resources/images/image.jpg" />" alt="image" />) in my jsp. I have this controller :
#Controller
public class FileUploadController implements ServletContextAware {
private ServletContext servletContext;
private String rootPath;
#RequestMapping(value = "/uploadSingleFile", method = RequestMethod.GET)
public ModelAndView uploadSingleFileFormDisplay() {
return new ModelAndView("uploadSingleFile");
}
#RequestMapping(value = "/uploadSingleFile", method = RequestMethod.POST)
public #ResponseBody String uploadSingleFileHandler(#RequestParam("file") MultipartFile file) {
String filename = file.getOriginalFilename();
rootPath = servletContext.getRealPath("/") + "resources/uploads";
if (!file.isEmpty()) {
try {
Document document = new Document();
Image image = new Image();
byte[] bytes = file.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File(
rootPath + "/" + filename
)));
stream.write(bytes);
stream.close();
return "You successfully uploaded " + filename + "!";
} catch (Exception e) {
return "You failed to upload " + filename + " => " + e.getMessage();
}
} else {
return "You failed to upload " + filename + " because the file was empty.";
}
}
(...)
}
How do I build the rootPath to have an absolute path of my system like this : C:/absolute/path/to/webapp/resources or /absolute/path/to/webapp/resources ?
I would say it is not good idea. In fact src folder does not exist. The resources are moved on compile.
Moreover it's not good to place uploaded under web root. First because on restart the web root could be replaced with a new structure from WAR and because of security reasons. You get a potential hole letting something to be uploaded in the place where it could be run.
Instead define an upload path property and use it to store uploaded files and download them if necessary.
UPDATE:
#Value("${myUploadPath}")
private String upload;
and specify property in e.g. application.properties or as JVM argument on start
-DmyUploadPath="C:/absolute/path/to"
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!