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 ;)
Related
I am new in Spring-Boot...
I want to upload images or videos, and store them in a persistant folder "upload-storage" in the class-path of my project in the server. I don't want to store them in the database (20 Mo).
Spring-Boot store them in target/upload-storage.
That functions : I can show the videos on the view with the controller and Thymeleaf. I can close tomcat, close the browser, and open them again : that functions.
But the day after, upload-storage is disapeared !
I think that I don't use the good process.
But I found how to upload an image : ok. I found how to show images from a folder in class-path : ok. I found how to upload images to database. But nothing to store the uploaded images in a persistant folder.
Can you help me ? Can you tell me the good process ?
Some details :
I have an entity "video" to store name, extension, length,... of the video.
I have "VideoRepository" and "VideoService" to manage the requests with "Video".
I have a "StorageService" and "StorageServiceImpl" to manage the upload of video and images : It as to upload the video and save it in a folder called "upload-storage" : I will come back on it farther.
I have a videoForm.html first with a form to select a file and send it to "UploadController", then an other form to show the video, the datas extracted from the video, modify the name or add precisions, and send this form to a "VideoController" who save the entity.
A part of the code of "UploadController" :
`
#Controller
public class UploadController extends BaseController {
private final StorageService storageServiceImpl;
#Autowired
public UploadController(StorageService storageServiceImpl) {
this.storageServiceImpl = storageServiceImpl;
}
#PostMapping("/upload")
public String recupereUpload(#RequestParam("file") MultipartFile file,Model model){
String filename ="";
try {
final long limit = 200 * 1024 * 1024;
if (file.getSize() > limit) {
model.addAttribute("message", "Taille du fichier trop grand (>200MB)");
model.addAttribute("ok", false );
}
filename = storageServiceImpl.store(file);
model.addAttribute("filename", filename);
model.addAttribute("message", "Le téléchargement de " + filename+" est réussi !");
} catch (Exception e) {
model.addAttribute("message", "FAIL to upload " + filename + "!");
model.addAttribute("ok", false );
}
Video video = new Video();
model.addAttribute("ok", true );
model.addAttribute("video", video);
String baseName = storageServiceImpl.getBaseName(filename);
String ext = storageServiceImpl.getExtension(filename);
model.addAttribute("nom", baseName);
model.addAttribute("ext", ext);
model.addAttribute("nomorigin", filename);
model.addAttribute("size", Math.round(file.getSize()/1024));
String typExt = storageServiceImpl.getType(ext);
model.addAttribute("typExt", typExt);
return "elementVideo/videoForm";
}
`
"StorageServiceImpl" has different methods :
getExtension(String filename){...}
getType(String ext){...}
getType(String ext){...}
getBaseName(String filename){...}
The main method is store(MultipartFile file) {...} :
#Service
public class StorageServiceImpl implements StorageService {
private final Path storageLocation = Paths.get("upload-storage");
#Override
public String store(MultipartFile file) {
try {
// Vérification de l'existence :
if (file.isEmpty()) {
throw new Exception("Failed to store empty file " + file.getOriginalFilename() );
}
// Vérification de la nature et traitement du fichier uploadé :
String ext = getExtension(file.getOriginalFilename());
String[] extAutorise = {"mp4", "avi","ogg","ogv","jpg","jpeg","png","gif"};
String fileNameTarget ="";
if ( ArrayUtils.contains( extAutorise, ext)) {
//Définir le fichier destination :
fileNameTarget = file.getOriginalFilename();
fileNameTarget = fileNameTarget.replaceAll(" ", "_");
File dir = storageLocation.toFile();
String serverFile = dir.getAbsolutePath() + File.separator + fileNameTarget ;
try {
try (InputStream is = file.getInputStream();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(serverFile))
) {
int i;
while ((i = is.read()) != -1) {
stream.write(i);
}
stream.flush();
}
} catch (IOException e) {
System.out.println("error : " + e.getMessage());
}
}
return fileNameTarget;
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
`
With this code, a folder "upload-storage" is created at the root of the project.
The video is uploaded in this folder...
But in "videoForm.html", the code
<video id="video" th:src="'/upload-storage/'+${filename}" height="60"
autoplay="autoplay"></video>
shows nothing.
I have an other solution.
In StorageServiceImpl, I use the code :
private final String storageLocation = this.getClass().getResource("/static/").getPath();
at place of :
private final Path storageLocation = Paths.get("upload-storage");
then :
File dir = new File(storageLocation + File.separator + "upload-storage");
at place of :
File dir = storageLocation.toFile();
then :
File serverFile = new File(dir.getAbsolutePath() + File.separator + fileNameTarget);
at place of :
String serverFile = dir.getAbsolutePath() + File.separator + fileNameTarget ;
With this solution, upload-storage is created in target folder.
I use an other controller BaseController :
public class BaseController {
public static final String PARAM_BASE_URL = "baseURL";
public String getBaseURL(HttpServletRequest request){
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
}
}
`
UploadController extends this BaseController.
I add HttpServletRequest request in recupereUpload() :
#PostMapping("/upload")
public String recupereUpload(#RequestParam("file") MultipartFile file,
Model model, HttpServletRequest request ){
I add in the model sent by recupereUpload :
model.addAttribute(PARAM_BASE_URL, getBaseURL(request));
And at last, I can see my video in videoForm.html with the code :
<video id="video" th:src="${baseURL}+'/upload-storage/'+${filename}" height="60" autoplay="autoplay"></video>
I can close Tomcat, close Eclipse, close the machine, and open all again : all is preserved and I can see the video.
But some time later : all is disappeared.
There must be a better solution.
Can you help me ?
Why dont you use Spring Content for the video content portion of your solution? That way you won't need to implement any of the video content handling. Spring Content will provide this for you. To add Spring Content to your project:
Add Spring Content to your classpath.
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>
Associate content with your Video entity.
Video.java
#Entity
public class Video {
...
#ContentId
private String contentId;
#ContentLength
private Long contetLen;
#MimeType
private String mimeType;
...
Set up a "persistent folder" as the root of your video store. This is where uploaded videos will be stored/streamed from. Also create a VideoStore interface to describe to SC how you want to associate your content.
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() {
return new File("/path/to/your/videos");
}
#Bean
public FileSystemResourceLoader fsResourceLoader() throws Exception {
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
}
#StoreRestResource(path="videos")
public interface VideoStore extends ContentStore<Video,String> {
//
}
}
This is all you need to create a REST-based video service at /videos. Essentially, when your application starts, Spring Content will look at your dependencies (seeing Spring Content FS/REST), look at your VideoStore interface and inject an implementation of that interface based on the filesystem. It will also inject a controller that forwards http requests to that implementation as well. This saves you having to implement any of this yourself.
So...
POST /videos/{video-entity-id}
with a multipart/form-data request will store the video in /path/to/your/videos and associate it with the video entity whose id is video-entity-id.
GET /videos/{video-entity-id}
will fetch it again. This supports partial content requests or byte ranges; i.e. video streaming too.
and so on...support full CRUD.
There are a couple of getting started guides here. The reference guide is here. And there is a tutorial video here. The coding bit starts about 1/2 way through.
HTH
Did you enable the upload by adding the following property in the application.properties file?
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
I have written an article about how to upload a multipart file in spring boot using thymeleaf. Here is the service used for the upload.
package com.uploadMultipartfile.storage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
#Service
public class FileSystemStorageService implements StorageService
{
private final Path rootLocation;
#Autowired
public FileSystemStorageService(StorageProperties properties) {
this.rootLocation = Paths.get(properties.getUploadDir()).toAbsolutePath().normalize();
try {
Files.createDirectories(this.rootLocation);
} catch (Exception ex) {
throw new StorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
#Override
public String store(MultipartFile file)
{
// Normalize file name
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try
{
if (file.isEmpty())
{
throw new StorageException("Failed to store empty file " + file.getOriginalFilename());
}
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.rootLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
}
catch (IOException e)
{
throw new StorageException("Failed to store file " + file.getOriginalFilename(), e);
}
}
#Override
public void init()
{
try
{
Files.createDirectory(rootLocation);
}
catch (IOException e)
{
throw new StorageException("Could not initialize storage", e);
}
}
}
Here is a link to get the code of the application. http://mkaroune.e-monsite.com/pages/spring/spring-boot-multipart-file-upload.html
I am working with Spring-websocket and I have the following problem:
I am trying to put a placeholder inside a #MessageMapping annotation in order to get the url from properties. It works with #RequestMapping but not with #MessageMapping.
If I use this placeholder, the URL is null. Any idea or suggestion?
Example:
#RequestMapping(value= "${myProperty}")
#MessageMapping("${myProperty}")
Rossen Stoyanchev added placeholder support for #MessageMapping and #SubscribeMapping methods.
See Jira issue: https://jira.spring.io/browse/SPR-13271
Spring allows you to use property placeholders in #RequestMapping, but not in #MessageMapping. This is 'cause the MessageHandler. So, we need to override the default MessageHandler to do this.
WebSocketAnnotationMethodMessageHandler does not support placeholders and you need add this support yourself.
For simplicity I just created another WebSocketAnnotationMethodMessageHandler class in my project at the same package of the original, org.springframework.web.socket.messaging, and override getMappingForMethod method from SimpAnnotationMethodMessageHandler with same content, changing only how SimpMessageMappingInfo is contructed using this with this methods (private in WebSocketAnnotationMethodMessageHandler):
private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
These methods now will resolve value considering properties (calling resolveAnnotationValues method), so we need use something like this:
private String[] resolveAnnotationValues(final String[] destinationNames) {
final int length = destinationNames.length;
final String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = this.resolveAnnotationValue(destinationNames[i]);
}
return result;
}
private String resolveAnnotationValue(final String name) {
if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
return name;
}
final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();
final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return name;
}
final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
return result != null ? result.toString() : name;
}
You still need to define a PropertySourcesPlaceholderConfigurer bean in your configuration.
If you are using XML based configuration, include something like this:
<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />
If you are using Java based configuration, you can try in this way:
#Configuration
#PropertySources(value = #PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Obs.: in this case, url-mapping-config.properties file are in a gradle/maven project in src\main\resources\META-INF\spring folder and content look like this:
myPropertyWS=urlvaluews
This is my sample controller:
#Controller
public class WebSocketController {
#SendTo("/topic/test")
#MessageMapping("${myPropertyWS}")
public String test() throws Exception {
Thread.sleep(4000); // simulated delay
return "OK";
}
}
With default MessageHandler startup log will print something like this:
INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
And with our MessageHandler now print this:
INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
See in this gist the full WebSocketAnnotationMethodMessageHandler implementation.
EDIT: this solution resolves the problem for versions before 4.2 GA. For more information, see this jira.
Update :
Now I understood what you mean, but I think that is not possible(yet).
Documentation does not mention anything related to Path mapping URIs.
Old answer
Use
#MessageMapping("/handler/{myProperty}")
instead of
#MessageMapping("/handler/${myProperty}")
And use it like this:
#MessageMapping("/myHandler/{username}")
public void handleTextMessage(#DestinationVariable String username,Message message) {
//do something
}
#MessageMapping("/chat/{roomId}")
public Message handleMessages(#DestinationVariable("roomId") String roomId, #Payload Message message, Traveler traveler) throws Exception {
System.out.println("Message received for room: " + roomId);
System.out.println("User: " + traveler.toString());
// store message in database
message.setAuthor(traveler);
message.setChatRoomId(Integer.parseInt(roomId));
int id = MessageRepository.getInstance().save(message);
message.setId(id);
return message;
}
According to my requirement, I need to externalize the message.properties file (Keep outside from war file) and in same time it should be automatically re loadable on update.
So I achieved both by following code and its working fine with Jetty Server. But in when I use Tomcat Server that externalized property file is not picking up by the system, instead its uses only the file inside the war.
public final class Messages
{
public static final String BUNDLE_NAME = "com.sample.project.core.ui.resources.messages";
// private static ResourceBundle resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
private static ResourceBundle resourceBundle;
private static final Logger LOGGER = Logger.getLogger(Messages.class);
private static ReloadableResourceBundleMessageSource messageSource;
static
{
try
{
FileInputStream fis =
new FileInputStream(System.getProperty("resources.messages.file.path"));
resourceBundle = new PropertyResourceBundle(fis);
}
catch (FileNotFoundException e)
{
LOGGER.error("messages.properties file not found: " + e);
resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
}
catch (Exception e)
{
LOGGER.error("messages.properties file reading failed: " + e);
resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
}
}
private Messages()
{
}
/**
* <p>
* setter methos to ReloadableResourceBundleMessageSource object.
* </p>
*
*
* #param inMessageSource
* set reloadable resources bundle
**/
public static void setMessageSource(final ReloadableResourceBundleMessageSource inMessageSource)
{
Messages.messageSource = inMessageSource;
Messages.messageSource.setBasename(System.getProperty("resources.messages.file.path"));
}
/**
* <p>
* Resolve a message by a key and argument replacements.
* </p>
*
* #see MessageFormat#format(String, Object...)
* #param key
* the message to look up
* #param arguments
* optional message arguments
* #return the resolved message
**/
public static String getMessage(final String key, final Object... arguments)
{
try
{
if (messageSource != null)
{
return messageSource.getMessage(key, arguments, Locale.getDefault());
}
else
{
if (arguments != null)
return MessageFormat.format(resourceBundle.getString(key), arguments);
return resourceBundle.getString(key);
}
}
catch (NoSuchMessageException e)
{
LOGGER.error("Message key not found: " + key);
return '!' + key + '!';
}
catch (MissingResourceException e)
{
LOGGER.error("Message key not found: " + key);
return '!' + key + '!';
}
}
}
(Here file path I'm passing as a VM argument using "resources.messages.file.path" key)
First I thought it was a problem with accessing the file system and tried many ways. Then I heard about catalina.policy file and I added some lines like this..
grant codeBase "file:${catalina.base}/webapps/sample.war/-" {
permission java.security.AllPermission;
permission java.io.FilePermission "file:${catalina.base}${file.separator}webapps${file.separator}messages.properties", "read, write";
permission java.util.PropertyPermission "resources.messages.file.path", "read";
}
But non of them gave me luck. I'm desperate. Any idea what this issue is? Please help me. Thank you in advance. (Tested on Tomcat6)
Finally I found where I screwed up.
This setter method of messageSource should not be static and I removed static access of messageSource
messageSource = inMessageSource;
messageSource.setBasename(System.getProperty("resources.messages.file.path"));
Now code is working fine. And no need of that permission entry in catalina.policy file. Thanks to everyone who helped me.
Important : This question is completely useless for any Spring version higher than 3.0.4 as the issue discussed in this thread had been fixed in that version a long ago and is no longer reproducible in subsequent versions of Spring.
I'm using Spring version 3.0.2. I need to upload multiple files using the multiple="multiple" attribute of a file browser such as,
<input type="file" id="myFile" name="myFile" multiple="multiple"/>
(and not using multiple file browsers something like the one stated by this answer, it indeed works I tried).
Although no versions of Internet Explorer supports this approach unless an appropriate jQuery plugin/widget is used, I don't care about it right now (since most other browsers support this).
This works fine with commons fileupload but in addition to using RequestMethod.POST and RequestMethod.GET methods, I also want to use other request methods supported and suggested by Spring like RequestMethod.PUT and RequestMethod.DELETE in their own appropriate places. For this to be so, I have configured Spring with HiddenHttpMethodFilter which goes fine as this question indicates.
but it can upload only one file at a time even though multiple files in the file browser are chosen. In the Spring controller class, a method is mapped as follows.
#RequestMapping(method={RequestMethod.POST}, value={"admin_side/Temp"})
public String onSubmit(#RequestParam("myFile") List<MultipartFile> files, #ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) throws IOException, FileUploadException {
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
}
Even with the request parameter #RequestParam("myFile") List<MultipartFile> files which is a List of type MultipartFile (it can always have only one file at a time).
I could find a strategy which is likely to work with multiple files on this blog. I have gone through it carefully.
The solution below the section SOLUTION 2 – USE THE RAW REQUEST says,
If however the client insists on using the same form input name such
as ‘files[]‘ or ‘files’ and then populating that name with multiple
files then a small hack is necessary as follows. As noted above Spring
2.5 throws an exception if it detects the same form input name of type file more than once. CommonsFileUploadSupport – the class which throws
that exception is not final and the method which throws that exception
is protected so using the wonders of inheritance and subclassing one
can simply fix/modify the logic a little bit as follows. The change
I’ve made is literally one word representing one method invocation
which enables us to have multiple files incoming under the same form
input name.
It attempts to override the method
protected MultipartParsingResult parseFileItems(List fileItems, String encoding){}
of the abstract class CommonsFileUploadSupport by extending the class CommonsMultipartResolver such as,
package multipartResolver;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.fileupload.FileItem;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
public final class MultiCommonsMultipartResolver extends CommonsMultipartResolver {
public MultiCommonsMultipartResolver() {}
public MultiCommonsMultipartResolver(ServletContext servletContext) {
super(servletContext);
}
#Override
#SuppressWarnings("unchecked")
protected MultipartParsingResult parseFileItems(List fileItems, String encoding) {
Map<String, MultipartFile> multipartFiles = new HashMap<String, MultipartFile>();
Map multipartParameters = new HashMap();
// Extract multipart files and multipart parameters.
for (Iterator it = fileItems.iterator(); it.hasNext();) {
FileItem fileItem = (FileItem) it.next();
if (fileItem.isFormField()) {
String value = null;
if (encoding != null) {
try {
value = fileItem.getString(encoding);
} catch (UnsupportedEncodingException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode multipart item '" + fileItem.getFieldName()
+ "' with encoding '" + encoding + "': using platform default");
}
value = fileItem.getString();
}
} else {
value = fileItem.getString();
}
String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName());
if (curParam == null) {
// simple form field
multipartParameters.put(fileItem.getFieldName(), new String[]{value});
} else {
// array of simple form fields
String[] newParam = StringUtils.addStringToArray(curParam, value);
multipartParameters.put(fileItem.getFieldName(), newParam);
}
} else {
// multipart file field
CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
if (multipartFiles.put(fileItem.getName(), file) != null) {
throw new MultipartException("Multiple files for field name [" + file.getName()
+ "] found - not supported by MultipartResolver");
}
if (logger.isDebugEnabled()) {
logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize()
+ " bytes with original filename [" + file.getOriginalFilename() + "], stored "
+ file.getStorageDescription());
}
}
}
return new MultipartParsingResult(multipartFiles, multipartParameters);
}
}
What happens is that the last line in the method parseFileItems() (the return statement) i.e.
return new MultipartParsingResult(multipartFiles, multipartParameters);
causes a compile-time error because the first parameter multipartFiles is a type of Map implemented by HashMap but in reality, it requires a parameter of type MultiValueMap<String, MultipartFile>
It is a constructor of a static class inside the abstract class CommonsFileUploadSupport,
public abstract class CommonsFileUploadSupport {
protected static class MultipartParsingResult {
public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {}
}
}
The reason might be - this solution is about the Spring version 2.5 and I'm using the Spring version 3.0.2 which might be inappropriate for this version.
I however tried to replace the Map with MultiValueMap in various ways such as the one shown in the following segment of code,
MultiValueMap<String, MultipartFile>mul=new LinkedMultiValueMap<String, MultipartFile>();
for(Entry<String, MultipartFile>entry:multipartFiles.entrySet()) {
mul.add(entry.getKey(), entry.getValue());
}
return new MultipartParsingResult(mul, multipartParameters);
but no success. I'm not sure how to replace Map with MultiValueMap and even doing so could work either. After doing this, the browser shows the Http response,
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect
().
Apache Tomcat/6.0.26
I have tried to shorten the question as possible as I could and I haven't included unnecessary code.
How could be made it possible to upload multiple files after Spring has been configured with HiddenHttpMethodFilter?
That blog indicates that It is a long standing, high priority bug.
If there is no solution regarding the version 3.0.2 (3 or higher) then I have to disable Spring support forever and continue to use commons-fileupolad as suggested by the third solution on that blog omitting the PUT, DELETE and other request methods forever.
Very little changes to the code in the parseFileItems() method inside the class MultiCommonsMultipartResolver might make it upload multiple files but I couldn't succeed in my attempts (again with the Spring version 3.0.2 (3 or higher)).
For upload multiple files in one request I used this code:
I have such jsp:
<p>Select files to upload. Press Add button to add more file inputs.</p>
<table>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
</table>
<br/><input type="submit" value="Upload" />
File upload class bean:
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class FileUploadForm {
private CommonsMultipartFile [] files;
public CommonsMultipartFile[] getFiles() {
return files;
}
public void setFiles( CommonsMultipartFile[] files ) {
this.files = files;
}
}
Controller:
#Controller
#RequestMapping("/upload")
public class FileUploadController {
#RequestMapping(method = RequestMethod.GET)
public String displayForm(ModelMap modelMap) {
modelMap.addAttribute( new FileUploadForm() );
return "uploadForm.jsp";
}
#RequestMapping(method = RequestMethod.POST)
public String save(FileUploadForm uploadForm) {
CommonsMultipartFile[] files = uploadForm.getFiles();
if(files != null && files.length != 0) {
for(MultipartFile file : files) {
System.out.println( file.getOriginalFilename() );
}
}
return "success.jsp";
}
}
This code allows to upload multiple files in one request,
and be possible to get instance of CommonsMultipartFile for each file.
The issue as mentioned in the question was fixed as of Spring 3.0.4. As such, if you happened to use that version or higher (yes, it is 4.x.x now), you would not need to read this question/answer(s) anymore.
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);