Spring MVC save uploaded MultipartFile to specific folder - spring

I want to save uploaded images to a specific folder in a Spring 3 MVC application deployed on Tomcat
My problem is that I cannot save the uploaded images files to the host where the appliciation is running.
Here is what I tried:
private void saveFile(MultipartFile multipartFile, int id) throws Exception {
String destination = "/images/" + id + "/" + multipartFile.getOriginalFilename();
File file = new File(destination);
multipartFile.transferTo(file);
}
Result: FileNotFoundException - Yes sure, I do want create this file!?!
I tried it using the context.getRealPath or getResources("destination"), but without any success.
How can I create a new file in a specific folder of my app with the content of my multipart file?

This code will surely help you.
String filePath = request.getServletContext().getRealPath("/");
multipartFile.transferTo(new File(filePath));

Let's create the uploads directory in webapp and save files in webapp/uploads:
#RestController
public class GreetingController {
private final static Logger log = LoggerFactory.getLogger(GreetingController.class);
#Autowired
private HttpServletRequest request;
#RequestMapping(value = "/uploadfile", method = RequestMethod.POST)
public
#ResponseBody
ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
try {
String uploadsDir = "/uploads/";
String realPathtoUploads = request.getServletContext().getRealPath(uploadsDir);
if(! new File(realPathtoUploads).exists())
{
new File(realPathtoUploads).mkdir();
}
log.info("realPathtoUploads = {}", realPathtoUploads);
String orgName = file.getOriginalFilename();
String filePath = realPathtoUploads + orgName;
File dest = new File(filePath);
file.transferTo(dest);
the code
String realPathtoUploads = request.getServletContext().getRealPath(uploadsDir);
returns me next path if I run the app from Idea IDE
C:\Users\Iuliia\IdeaProjects\ENumbersBackend\src\main\webapp\uploads\
and next path if I make .war and run it under Tomcat:
D:\Programs_Files\apache-tomcat-8.0.27\webapps\enumbservice-0.2.0\uploads\
My project structure:

You can get the inputStream from multipartfile and copy it to any directory you want.
public String write(MultipartFile file, String fileType) throws IOException {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss-"));
String fileName = date + file.getOriginalFilename();
// folderPath here is /sismed/temp/exames
String folderPath = SismedConstants.TEMP_DIR + fileType;
String filePath = folderPath + File.separator + fileName;
// Copies Spring's multipartfile inputStream to /sismed/temp/exames (absolute path)
Files.copy(file.getInputStream(), Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);
return filePath;
}
This works for both Linux and Windows.

I saw a spring 3 example using xml configuration (note this does not wok for spring 4.2.*): http://www.devmanuals.com/tutorials/java/spring/spring3/mvc/spring3-mvc-upload-file.html
`
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000" />
<property name="uploadTempDir" ref="uploadDirResource" />
</bean>
<bean id="uploadDirResource" class="org.springframework.core.io.FileSystemResource">
<constructor-arg>
<value>C:/test111</value>
</constructor-arg>
</bean>

String ApplicationPath =
ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("");
This is how to get the real path of App in Spring (without using response, session ...)

The following worked for me on ubuntu:
String filePath = request.getServletContext().getRealPath("/");
File f1 = new File(filePath+"/"+multipartFile.getOriginalFilename());
multipartFile.transferTo(f1);

Related

Spring boot ResourceUtils read file content is giving Path Traversal vulnerability

I am building a spring boot application where i need to read json files for my component tests. I have a utility method which takes the name of the file and reads the content using ResourceUtils. Here is the code:
public static String getContent(String path) throws IOException {
File file = ResourceUtils.getFile(MyTest.class.getResource(path));
String content = new String(Files.readAllBytes(file.toPath()));
return content;
}
The checkmarx is reporting the above code as "This may cause a Path
Traversal vulnerability."
How to fix this?
Thanks
See this example for path traversal vulnerability Path Traversal
To fix this change it something like
private static final String BASE_PATH ="/yourbasepath/somewherewherefileisstored";
public static String getContent(String path) throws IOException {
File file = new File(BASE_PATH, path);
if (file.getCanonicalPath().startsWith(BASE_PATH)){
String content = new String(Files.readAllBytes(file.toPath()));
return content;
}
else{
//throw some error
}
}

How to add file in Solr?

I use Apache Solr so that I can work with files, I can add regular text fields via Spring, but I don’t know how to add TXT / pdf
#SolrDocument(solrCoreName = "accounting")
public class Accounting {
#Id
#Field
private String id;
#Field
private File txtFile;
#Field
private String docType;
#Field
private String docTitle;
public Accounting() {
}
public Accounting(String id, String docType, String docTitle) {
this.id = id;
this.docTitle = docTitle;
this.docType = docType;
}
here is the problem with the txtFile field
<field name="docTitle" type="strings"/>
<field name="docType" type="strings"/>
These fields that I manually added to schema.xml, I can not figure out how to add a field here that will be responsible for the file, for example, I will add here a txt file, how to do it? Thank you very much. And do I correctly declare the field private File txtFile; in the entity for the file?
Solr will not store the actual file anywhere. Depending on your config it can store the binary content though. Using the extract request handler Apache Solr which relies on Apache Tika to extract the content from the document.
You can try something like below code. The current code is not using anything from the springboot. Here the content is read from the pdf document and then the data is indexed into solr along with id and filename. I have used the tika apis to extract the content of the pdf.
public static void main(final String[] args) throws IOException, TikaException, SAXException {
String urlString = "http://localhost:8983/solr/TestCore1";
SolrClient solr = new HttpSolrClient.Builder(urlString).build();
BodyContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
File file = new File("C://Users//abhijitb//Desktop//TestDocument.pdf");
FileInputStream inputstream = new FileInputStream(file);
ParseContext pcontext = new ParseContext();
// parsing the document using PDF parser
PDFParser pdfparser = new PDFParser();
pdfparser.parse(inputstream, handler, metadata, pcontext);
// getting the content of the document
//System.out.println("Contents of the PDF :" + handler.toString());
try {
String fileName = file.getName();
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "123456");
document.addField("title", fileName);
document.addField("text", handler.toString());
solr.add(document);
solr.commit();
} catch (SolrServerException | IOException e) {
e.printStackTrace();
}
}
Once you index the data, it can be verified on the solr admin page by querying for it.
Please find the image for your reference.

How to use uploaded images inside JSP pages (Spring MVC)

I'm implementing a web application using Spring MVC. I'm trying to implement the module that allows to upload images. I'm using Apache Commons FileUpload and this is the controller that handle the post request:
/**
* Upload single file using Spring Controller
*/
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public String uploadFileHandler(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
String fileContentType = file.getContentType();
if (contentTypes.contains(fileContentType)) {
// You have the correct extension
// rest of your code here
try {
byte[] bytes = file.getBytes();
// Creating the directory to store file
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "bills");
if (!dir.exists())
dir.mkdirs();
// Create the file on server
File serverFile = new File(dir.getAbsolutePath()
+ File.separator + name);
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();
System.out.println("Server File Location="
+ serverFile.getAbsolutePath());
return "redirect:/";
} catch (Exception e) {
//TODO handle error
}
} else {
//TODO handle error
}
} else {
//TODO handle error
}
}
My first doubt is where should i save the images uploaded? Right now the directory is inside a GlassFish folder, is it ok? And I don't know why but the uploaded picture has no extension... is a simple file without any extension!
Now I want to let the user access these images but I don't know how to insert those inside the JSP page. I know that I should save the path inside the database relating it to a specified user but I don't know what to do next. Does anyone have any suggestions? Thank you very much!
About the way how to use uploaded images, I found this solution that seems working but I don't know if it's the best one or not. Inside the jsp file I have this:
<spring:url value="/file/download/" var="url"/>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="${url}${imageName}"/>
</body>
</html>
Where imageName is a value inside the model that contains, as the name clearly says, the name of the file, and url contains the path of the service that returns the stream of the image. This HTTP request is handled by the following controller:
public #ResponseBody byte[] getImageWithMediaType(#PathVariable("name") String name) throws IOException {
String rootPath = System.getProperty("catalina.home")
String partialPath = File.separator + "bills" + File.separator + name + ".png"
FileInputStream file = new FileInputStream(rootPath + partialPath);
InputStream is = new BufferedInputStream(file);
return IOUtils.toByteArray(is);
}
Do you think this is the best way to do so?
Why dont you use Spring Content? Then you don't need to implement any of that controller code at all. Assuming you are using Spring Boot (let me know if you are not) then it would look something like this:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>content-fs-spring-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
SpringBootApplication.java
#SpringBootApplication
public class YourSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(YourSpringBootApplication.class, args);
}
#Configuration
#EnableFilesystemStores
public static class StoreConfig {
File filesystemRoot() {
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "bills");
if (!dir.exists())
dir.mkdirs();
}
// this bean is the spring resource loader that will be used by
// the product store
#Bean
public FileSystemResourceLoader fsResourceLoader() throws Exception
{
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
}
#StoreRestResource(path="upload")
public interface BillStore extends Store<String> {
//
}
}
Note that you are not writing any controller code here but this is enough to create a REST-based content service at /upload that actually supports full CRUD functionality (as well as video streaming in case that us useful to you). Create == POST, Read == GET (include byte-range support), Update == PUT, Delete == DELETE.
e.g.
POST /upload/my-image.jpg
will store the uploaded image to System.getProperty("catalina.home") + File.Separator + "bills" + "my-image/jpg".
I am assuming you want your users to eventually be able to view their images after upload, upload new versions and possibly delete as well. Given this /upload is probably not a great name. But it is what you used in the question so what I went with in my answer. IF you really do just want upload functionality then you can use Spring Security to make the other actions impossibly to perform.

Downloading uploaded files from Amazon S3 service in a Spring MVC web app

I have a problem of downloading my uploaded files from amazon s3 service. I have succesfully implemented the upload section, all I need is to download these file to my local hardrive the view them later. My application is a spring mvc application.
This is my controller to call the download service
#Controller
public class fileController{
#Autowired S3Service s3Service;
#Autowired AwsConfig awsConfig;
#Autowired Environment env;
#Autowired DocRepository docRepo;
#RequestMapping(value="downloadDocume")
public void downloadDocument(#RequestParam("docId") Long docId
,HttpServletRequest request ,HttpServletResponse response)){
Document doc = docRepo.findOne(docId);
String docName = doc.getAsset().getName();
String ASSET_PATH = awsConfig.getBaseUrl()+"/"+
awsConfig.getBucket()+"/";
if (Objects.equals(env.getProperty("spring.profiles.active"),"prod")){
ASSET_PATH= awsConfig.getBaseUrl()+"/"+
awsConfig.getBucket()+"/";
}
String filtered = StringUtils.delete(docName, ASSET_PATH);
String mimetype = request.getSession().getServletContext().getMimeType(filtered);
FileStream file = s3Service.getAssetByName("/Documents/", filtered);
response.setContentType(mimetype);
response.setContentLength((int) file.getSize());
response.setHeader("Content-Disposition","attachment; filename=\"" + docName +"\"");
FileCopyUtils.copy(file.getInputStream(), response.getOutputStream());
}
}
//This is my S3Sservice class with the download method
#Service
public class S3Service{
public FileStream getAssetByName(String path , String name)
throws FileNotFoundException{
AmazonS3Client s3 = new AmazonS3Client(
new BasicAWSCredentials(awsConfig.getAccessKey(), awsConfig.getSecretKey()));
s3.setEndpoint(awsConfig.getBaseUrl());
s3.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(true));
S3Object obj = s3.getObject(new GetObjectRequest(awsConfig.getBucket(), getS3Path(path) + name));
return new FileStream(obj.getObjectContent(), obj.getObjectMetadata().getContentLength());
}
}
Wow.. The solution was very simple.. I just used the the html download link and passed the parameters on my jsp like this. This is i my document.jsp
<a class="btn btn-primary" href="${document.asset.name}" download="${document.asset.name}">Download Document</a>
I change my downloadDocument() in my controller to look like this
public void downloadDocument(#RequestParam("docId") Long docId
,HttpServletRequest request ,HttpServletResponse response)){
Document doc = docRepo.findOne(docId);
model.addAtribute("document" , doc);
return "document";
}
}

JSF PrimeFaces - store uploaded file path in another bean's attribute

To sum things up: I want to upload file passed by user through a form with <p:fileUpload> using fileUploadController ManagedBean as its fileUploadListener to a certain path, then get this path along with filename and Store it somehow (the question is - how?) in the wydarzenieMB ManagedBean because I need filePath as a String in my addWydarzenie() method to store this path in a database and later use it.
I've got a table in my database named "Wydarzenie". It has many values like name etc. which I am specyfing in my JSF form for user to fill using primefaces. There is no problem with obtaining them, I just use ManagedBean to store input and later use this input to write into DataBase using addWydarzenie() method.
Part of utworzWydarzenie.xhtml form:
...
<h:outputLabel for="opis" value="Opis :" />
<p:inputTextarea id="opis" value="#{wydarzenieMB.opis}" label="opis">
...
<p:commandButton id="addWydarzenie" value="Zatwierdź" action="#{wydarzenieMB.addWydarzenie}" ajax="false"/>
My addWydarzenie() method in wydarzenieManagedBean:
public String addWydarzenie() {
try {
Wydarzenie wydarzenie = new Wydarzenie();
...
wydarzenie.setOpis(getOpis());
...
getWydarzenieServiceImpl().addWydarzenie(wydarzenie);
return SUCCESS;
} catch (DataAccessException e) {
e.printStackTrace();
}
return ERROR;
}
There is no problem with values like that where I use p:inputText for example, because I specify value of bean (for example: value="#{wydarzenieMB.opis}") in which I want to store them. Problem begins with my <p:fileUpload> field because:
1. I am not specyfing value there because there is no value returned(?) - only file uploaded
2. I am using fileUploadController ManagedBean to handle file uploading BUT I want the filePath String of uploaded file that it possess to be stored in my wydarzenieMB ManagedBean.
Here's my uploading file code from utworzWydarzenie.xhtml form:
...
<h:outputLabel for="plakat" value="Plakat :" />
<p:fileUpload id="plakat" update="messages" fileUploadListener="#{fileUploadController.handleFileUpload}" multiple="false" sizeLimit="1000000" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:growl id="messages" showDetail="true"/>
...
And my fileUploadController class:
#ManagedBean(name="fileUploadController")
#RequestScoped
public class FileUploadController
{
private String destination="E:/PROJEKT ZESPOLOWY/events/WebContent/resources/plakaty/";
private String sciezkaPliku = ""; // complete file path including destination and file name
public void handleFileUpload(FileUploadEvent event) throws IOException {
FacesMessage msg = new FacesMessage("Plik: ", event.getFile().getFileName() + " został poprawnie wysłany na serwer.");
FacesContext.getCurrentInstance().addMessage(null, msg);
String path = destination;
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss");
String name = fmt.format(new Date())
+ event.getFile().getFileName().substring(
event.getFile().getFileName().lastIndexOf('.'));
File file = new File(path + name);
sciezkaPliku += path + name; // I set file path here
InputStream is = event.getFile().getInputstream();
OutputStream out = new FileOutputStream(file);
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0)
out.write(buf, 0, len);
is.close();
out.close();
}
public String getSciezkaPliku() {
return sciezkaPliku;
}
public void setSciezkaPliku(String sciezkaPliku) {
this.sciezkaPliku = sciezkaPliku;
}
}
It is declared as a ManagedBean because I've tried to access its sciezkaPliku variable which is filepath from wydarzenieMB like this:
#ManagedProperty(value="#{fileUploadController.sciezkaPliku}")
private String plakat;
But it was always null. I also tried to get whole wydarzenieMB inside fileUploadController and use it's setter method:
#ManagedBean(name="fileUploadController")
#RequestScoped
public class FileUploadController
{
#ManagedProperty(value="#{wydarzenieMB}")
WydarzenieManagedBean w;
public void handleFileUpload(FileUploadEvent event) throws IOException {
...
sciezkaPliku += path + name; // I set file path here
w.setPlakat(sciezkaPliku);
...
}
...
}
But it was unsuccessful either. File is uploaded and saved in a specified folder without a problem, when I print sciezkaPliku from handleFileUpload it is ok, but when I am creating my new Wydarzenie using addWydarzenie() it is always null.
Any ideas?
If I've understood your question clearly, I suggest you to change your scope to "#SessionScoped" in FileUploadController. I had a same problem and it was solved by this way.
check it out and inform me of the result ;)

Resources