J'essaie de télécharger un fichier volumineux à l'aide de l'API Apache Commons File Upload "en continu".
J'utilise Apache Commons File Uploader et non l'uploader Spring Multipart par défaut, car il échoue lorsque nous téléchargeons des fichiers très volumineux (~ 2 Go). Je travaille sur une application SIG où de tels téléchargements de fichiers sont assez courants.
Le code complet de mon contrôleur de téléchargement de fichier est le suivant:
@Controller
public class FileUploadController {
@RequestMapping(value="/upload", method=RequestMethod.POST)
public void upload(HttpServletRequest request) {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
return;
}
//String filename = request.getParameter("name");
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
try {
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
} else {
System.out.println("File field " + name + " with file name " + item.getName() + " detected.");
// Process the input stream
OutputStream out = new FileOutputStream("incoming.gz");
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
}catch (FileUploadException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
@RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
Le problème est que la fonction getItemIterator(request)
renvoie toujours un itérateur qui ne contient aucun élément (c'est-à-dire iter.hasNext()
) renvoie toujours false
.
Mon fichier application.properties est le suivant:
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:19095/authdb
spring.datasource.username=georbis
spring.datasource.password=asdf123
logging.level.org.springframework.web=DEBUG
spring.jpa.hibernate.ddl-auto=update
multipart.maxFileSize: 128000MB
multipart.maxRequestSize: 128000MB
server.port=19091
La vue JSP pour le /uploader
est la suivante:
<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload">
File to upload: <input type="file" name="file"><br />
Name: <input type="text" name="name"><br /> <br />
Press here to upload the file!<input type="submit" value="Upload">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
Que pourrais-je faire de mal?
Grâce aux commentaires très utiles de M.Deinum, j'ai réussi à résoudre le problème. J'ai nettoyé une partie de mon message original et je l'affiche comme une réponse complète pour référence future.
La première erreur que j'ai commise a été de ne pas désactiver la valeur par défaut MultipartResolver
fournie par Spring. Cela finissait par traiter le résolveur HttpServeletRequest
par le résolveur avant que mon contrôleur ne puisse agir dessus.
Le moyen de le désactiver, grâce à M. Deinum, était le suivant:
multipart.enabled=false
Cependant, il y avait encore un autre piège caché qui m'attendait après cela. Dès que j'ai désactivé le résolveur multipart par défaut, j'ai commencé à obtenir l'erreur suivante lors d'une tentative de téléchargement:
Fri Sep 25 20:23:47 IST 2015
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
Dans ma configuration de sécurité, j'avais activé la protection CSRF. Cela a nécessité l'envoi de ma demande POST de la manière suivante:
<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}">
<input type="file" name="file"><br>
<input type="submit" value="Upload">
</form>
</body>
</html>
J'ai aussi un peu modifié mon contrôleur:
@Controller
public class FileUploadController {
@RequestMapping(value="/upload", method=RequestMethod.POST)
public @ResponseBody Response<String> upload(HttpServletRequest request) {
try {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
Response<String> responseObject = new Response<String>(false, "Not a multipart request.", "");
return responseObject;
}
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (!item.isFormField()) {
String filename = item.getName();
// Process the input stream
OutputStream out = new FileOutputStream(filename);
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
} catch (FileUploadException e) {
return new Response<String>(false, "File upload error", e.toString());
} catch (IOException e) {
return new Response<String>(false, "Internal server IO error", e.toString());
}
return new Response<String>(true, "Success", "");
}
@RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
où Response est juste un type de réponse générique simple que j'utilise:
public class Response<T> {
/** Boolean indicating if request succeeded **/
private boolean status;
/** Message indicating error if any **/
private String message;
/** Additional data that is part of this response **/
private T data;
public Response(boolean status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
// Setters and getters
...
}
Si vous utilisez une version récente de Spring Boot (j'utilise la version 2.0.0.M7), les noms de propriété ont alors été modifiés.
spring.servlet.multipart.maxFileSize = -1
spring.servlet.multipart.maxRequestSize = -1
spring.servlet.multipart.enabled = false
Si vous obtenez des exceptions StreamClosed dues à l'activation de plusieurs implémentations, la dernière option vous permet de désactiver l'implémentation Spring par défaut.
Essayez d’ajouter spring.http.multipart.enabled=false
dans application.properties file.
J'utilise kindeditor + springboot. Lorsque j'utilise la demande (MultipartHttpServletRequest). Je pourrais obtenir le fichier, mais j'utilise appeche-common-io: upload.parse (demande), la valeur de retour est null.
public BaseResult uploadImg(HttpServletRequest request,String type){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();