提交 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");
* you may not use this file except in compliance with the License.
......@@ -27,7 +27,7 @@ import org.springframework.core.io.InputStreamSource;
*
* <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
* 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.
*
* @author Juergen Hoeller
......@@ -50,6 +50,8 @@ public interface MultipartFile extends InputStreamSource {
* but it typically will not with any other than Opera.
* @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
* @see org.apache.commons.fileupload.FileItem#getName()
* @see org.springframework.web.multipart.commons.CommonsMultipartFile#setPreserveFilename
*/
String getOriginalFilename();
......@@ -81,7 +83,7 @@ public interface MultipartFile extends InputStreamSource {
/**
* 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
* @throws IOException in case of access errors (if the temporary store fails)
*/
......@@ -91,18 +93,22 @@ public interface MultipartFile extends InputStreamSource {
/**
* Transfer the received file to the given destination file.
* <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.
* If the destination file already exists, it will be deleted first.
* <p>If the file has been moved in the filesystem, this operation cannot
* be invoked again. Therefore, call this method just once to be able to
* work with any storage mechanism.
* <p><strong>Note:</strong> when using Servlet 3.0 multipart support you
* need to configure the location relative to which files will be copied
* as explained in {@link javax.servlet.http.Part#write}.
* @param dest the destination file
* filesystem, or save memory-held contents to the destination file. If the
* destination file already exists, it will be deleted first.
* <p>If the target file has been moved in the filesystem, this operation
* cannot be invoked again afterwards. Therefore, call this method just once
* in order to work with any storage mechanism.
* <p><b>NOTE:</b> Depending on the underlying provider, temporary storage
* may be container-dependent, including the base directory for relative
* destinations specified here (e.g. with Servlet 3.0 multipart handling).
* 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 IllegalStateException if the file has already been moved
* 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;
......
/*
* 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");
* you may not use this file except in compliance with the License.
......@@ -165,7 +165,7 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
if (logger.isDebugEnabled()) {
String action = "transferred";
if (!this.fileItem.isInMemory()) {
action = isAvailable() ? "copied" : "moved";
action = (isAvailable() ? "copied" : "moved");
}
logger.debug("Multipart file '" + getName() + "' with original filename [" +
getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
......@@ -173,14 +173,18 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
}
}
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) {
// From I/O operations within FileItem.write
throw ex;
}
catch (Exception ex) {
logger.error("Could not transfer to file", ex);
throw new IOException("Could not transfer to file: " + ex.getMessage());
throw new IOException("File transfer failed", ex);
}
}
......
......@@ -17,6 +17,7 @@
package org.springframework.web.multipart.support;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
......@@ -307,6 +308,15 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
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.
先完成此消息的编辑!
想要评论请 注册