提交 b73153cd 编写于 作者: J Juergen Hoeller

StandardMultipartFile.transferTo falls back to manual copy

Issue: SPR-15257
上级 0662dbf0
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -27,7 +27,7 @@ import org.springframework.core.io.InputStreamSource; ...@@ -27,7 +27,7 @@ import org.springframework.core.io.InputStreamSource;
* *
* <p>The file contents are either stored in memory or temporarily on disk. * <p>The file contents are either stored in memory or temporarily on disk.
* In either case, the user is responsible for copying file contents to a * In either case, the user is responsible for copying file contents to a
* session-level or persistent store as and if desired. The temporary storages * session-level or persistent store as and if desired. The temporary storage
* will be cleared at the end of request processing. * will be cleared at the end of request processing.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
...@@ -50,6 +50,8 @@ public interface MultipartFile extends InputStreamSource { ...@@ -50,6 +50,8 @@ public interface MultipartFile extends InputStreamSource {
* but it typically will not with any other than Opera. * but it typically will not with any other than Opera.
* @return the original filename, or the empty String if no file has been chosen * @return the original filename, or the empty String if no file has been chosen
* in the multipart form, or {@code null} if not defined or not available * in the multipart form, or {@code null} if not defined or not available
* @see org.apache.commons.fileupload.FileItem#getName()
* @see org.springframework.web.multipart.commons.CommonsMultipartFile#setPreserveFilename
*/ */
String getOriginalFilename(); String getOriginalFilename();
...@@ -81,7 +83,7 @@ public interface MultipartFile extends InputStreamSource { ...@@ -81,7 +83,7 @@ public interface MultipartFile extends InputStreamSource {
/** /**
* Return an InputStream to read the contents of the file from. * Return an InputStream to read the contents of the file from.
* The user is responsible for closing the stream. * <p>The user is responsible for closing the returned stream.
* @return the contents of the file as stream, or an empty stream if empty * @return the contents of the file as stream, or an empty stream if empty
* @throws IOException in case of access errors (if the temporary store fails) * @throws IOException in case of access errors (if the temporary store fails)
*/ */
...@@ -91,18 +93,22 @@ public interface MultipartFile extends InputStreamSource { ...@@ -91,18 +93,22 @@ public interface MultipartFile extends InputStreamSource {
/** /**
* Transfer the received file to the given destination file. * Transfer the received file to the given destination file.
* <p>This may either move the file in the filesystem, copy the file in the * <p>This may either move the file in the filesystem, copy the file in the
* filesystem, or save memory-held contents to the destination file. * filesystem, or save memory-held contents to the destination file. If the
* If the destination file already exists, it will be deleted first. * destination file already exists, it will be deleted first.
* <p>If the file has been moved in the filesystem, this operation cannot * <p>If the target file has been moved in the filesystem, this operation
* be invoked again. Therefore, call this method just once to be able to * cannot be invoked again afterwards. Therefore, call this method just once
* work with any storage mechanism. * in order to work with any storage mechanism.
* <p><strong>Note:</strong> when using Servlet 3.0 multipart support you * <p><b>NOTE:</b> Depending on the underlying provider, temporary storage
* need to configure the location relative to which files will be copied * may be container-dependent, including the base directory for relative
* as explained in {@link javax.servlet.http.Part#write}. * destinations specified here (e.g. with Servlet 3.0 multipart handling).
* @param dest the destination file * For absolute destinations, the target file may get renamed/moved from its
* temporary location or newly copied, even if a temporary copy already exists.
* @param dest the destination file (typically absolute)
* @throws IOException in case of reading or writing errors * @throws IOException in case of reading or writing errors
* @throws IllegalStateException if the file has already been moved * @throws IllegalStateException if the file has already been moved
* in the filesystem and is not available anymore for another transfer * in the filesystem and is not available anymore for another transfer
* @see org.apache.commons.fileupload.FileItem#write(File)
* @see javax.servlet.http.Part#write(String)
*/ */
void transferTo(File dest) throws IOException, IllegalStateException; void transferTo(File dest) throws IOException, IllegalStateException;
......
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -165,7 +165,7 @@ public class CommonsMultipartFile implements MultipartFile, Serializable { ...@@ -165,7 +165,7 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
String action = "transferred"; String action = "transferred";
if (!this.fileItem.isInMemory()) { if (!this.fileItem.isInMemory()) {
action = isAvailable() ? "copied" : "moved"; action = (isAvailable() ? "copied" : "moved");
} }
logger.debug("Multipart file '" + getName() + "' with original filename [" + logger.debug("Multipart file '" + getName() + "' with original filename [" +
getOriginalFilename() + "], stored " + getStorageDescription() + ": " + getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
...@@ -173,14 +173,18 @@ public class CommonsMultipartFile implements MultipartFile, Serializable { ...@@ -173,14 +173,18 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
} }
} }
catch (FileUploadException ex) { catch (FileUploadException ex) {
throw new IllegalStateException(ex.getMessage()); throw new IllegalStateException(ex.getMessage(), ex);
}
catch (IllegalStateException ex) {
// Pass through when coming from FileItem directly
throw ex;
} }
catch (IOException ex) { catch (IOException ex) {
// From I/O operations within FileItem.write
throw ex; throw ex;
} }
catch (Exception ex) { catch (Exception ex) {
logger.error("Could not transfer to file", ex); throw new IOException("File transfer failed", ex);
throw new IOException("Could not transfer to file: " + ex.getMessage());
} }
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.web.multipart.support; package org.springframework.web.multipart.support;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
...@@ -307,6 +308,15 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe ...@@ -307,6 +308,15 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
@Override @Override
public void transferTo(File dest) throws IOException, IllegalStateException { public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath()); this.part.write(dest.getPath());
if (dest.isAbsolute() && !dest.exists()) {
// Servlet 3.0 Part.write is not guaranteed to support absolute file paths:
// may translate the given path to a relative location within a temp dir
// (e.g. on Jetty whereas Tomcat and Undertow detect absolute paths).
// At least we offloaded the file from memory storage; it'll get deleted
// from the temp dir eventually in any case. And for our user's purposes,
// we can manually copy it to the requested location as a fallback.
FileCopyUtils.copy(this.part.getInputStream(), new FileOutputStream(dest));
}
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册