StandardMultipartFile.transferTo falls back to manual copy
Issue: SPR-15257
This commit is contained in:
		
							parent
							
								
									0662dbf044
								
							
						
					
					
						commit
						b73153cd7c
					
				| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue