Revision: 411
http://svn.sourceforge.net/stripes/?rev=411&view=rev
Author: tfenne
Date: 2006-09-25 17:49:22 -0700 (Mon, 25 Sep 2006)
Log Message:
-----------
Fix for STS-270: FileBean.save() fails when target file is on a different file
system.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/action/FileBean.java
Added Paths:
-----------
trunk/tests/src/net/sourceforge/stripes/action/
trunk/tests/src/net/sourceforge/stripes/action/FileBeanTests.java
Modified: trunk/stripes/src/net/sourceforge/stripes/action/FileBean.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/action/FileBean.java
2006-09-06 11:43:16 UTC (rev 410)
+++ trunk/stripes/src/net/sourceforge/stripes/action/FileBean.java
2006-09-26 00:49:22 UTC (rev 411)
@@ -14,10 +14,7 @@
*/
package net.sourceforge.stripes.action;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.FileInputStream;
+import java.io.*;
/**
* <p>Represents a file that was submitted as part of an HTTP POST request.
Provides methods for
@@ -97,8 +94,10 @@
}
/**
- * Saves the uploaded file to the location on disk represented by File.
This is currently
- * implemented as a simple rename of the underlying file that was created
during upload.
+ * Saves the uploaded file to the location on disk represented by File.
First attemps a
+ * simple rename of the underlying file that was created during upload as
this is the
+ * most efficient route. If the rename fails an attempt is made to copy
the file bit
+ * by bit to the new File and then the temporary file is removed.
*
* @param toFile a File object representing a location
* @throws IOException if the save will fail for a reason that we can
detect up front, for
@@ -119,22 +118,50 @@
+ this.file.getAbsolutePath() + " - writability is
required to move the file.");
}
+ File parent = toFile.getAbsoluteFile().getParentFile();
if (toFile.exists() && !toFile.canWrite()) {
throw new IOException("Cannot overwrite existing file at "+
toFile.getAbsolutePath());
}
- else if (!toFile.exists() &&
!toFile.getAbsoluteFile().getParentFile().canWrite()) {
+ else if (!parent.exists() && !parent.mkdirs()) {
+ throw new IOException("Parent directory of specified file does not
exist and cannot " +
+ " be created. File location supplied: " +
toFile.getAbsolutePath());
+ }
+ else if (!toFile.exists() && !parent.canWrite()) {
throw new IOException("Cannot create new file at location: " +
toFile.getAbsolutePath());
}
this.saved = this.file.renameTo(toFile);
+ // If the rename didn't work, try copying the darn thing bit by bit
if (this.saved == false) {
- throw new IOException("Tried to save file [" +
this.file.getAbsolutePath() + "] to file ["
- + toFile.getAbsolutePath() + "but got a false from
File.renameTo(File).");
+ saveViaCopy(toFile);
}
}
/**
+ * Attempts to save the uploaded file to the specified file by performing
a stream
+ * based copy. This is only used when a rename cannot be executed, e.g.
because the
+ * target file is on a different file system than the temporary file.
+ *
+ * @param toFile the file to save to
+ */
+ protected void saveViaCopy(File toFile) throws IOException {
+ BufferedOutputStream out = new BufferedOutputStream(new
FileOutputStream(toFile));
+ BufferedInputStream in = new BufferedInputStream(new
FileInputStream(this.file));
+
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+
+ in.close();
+ out.close();
+
+ this.file.delete();
+ this.saved = true;
+ }
+
+ /**
* Deletes the temporary file associated with this file upload if one
still exists. If save()
* has already been called then there is no temporary file any more, and
this is a no-op.
*
Added: trunk/tests/src/net/sourceforge/stripes/action/FileBeanTests.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/action/FileBeanTests.java
(rev 0)
+++ trunk/tests/src/net/sourceforge/stripes/action/FileBeanTests.java
2006-09-26 00:49:22 UTC (rev 411)
@@ -0,0 +1,105 @@
+package net.sourceforge.stripes.action;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
+import org.testng.Assert;
+
+import java.io.*;
+
+/**
+ * Basic set of tests for the FileBean object.
+ *
+ * @author Tim Fennell
+ */
+public class FileBeanTests {
+ public static final String[] LINES = {"Hello World!", "How have you
been?"};
+ File from, to;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setupFiles() throws IOException {
+ // The from file
+ this.from = File.createTempFile("foo", "bar");
+ FileWriter out = new FileWriter(this.from);
+ out.write(LINES[0]);
+ out.write('\n');
+ out.write(LINES[1]);
+ out.write('\n');
+ out.close();
+
+ // A to file
+ this.to = new File(System.getProperty("java.io.tmpdir"), "foo-" +
System.currentTimeMillis());
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void cleanupFiles() {
+ if (this.from != null && this.from.exists()) this.from.delete();
+ if (this.to != null && this.to.exists()) this.to.delete();
+ }
+
+ /** Helper method to assert contents of post-copy file. */
+ private void assertContents(File toFile) throws IOException {
+ BufferedReader in = new BufferedReader(new FileReader(toFile));
+ Assert.assertEquals(in.readLine(), LINES[0]);
+ Assert.assertEquals(in.readLine(), LINES[1]);
+ Assert.assertNull(in.readLine());
+ }
+
+ @Test(groups="fast")
+ public void testBasicSave() throws Exception {
+ FileBean bean = new FileBean(from, "text/plain", "somefile.txt");
+
+ bean.save(this.to);
+ Assert.assertTrue(this.to.exists());
+ Assert.assertFalse(this.from.exists());
+ assertContents(this.to);
+ }
+
+ @Test(groups="fast")
+ public void testSaveByCopy() throws Exception {
+ FileBean bean = new FileBean(from, "text/plain", "somefile.txt");
+
+ bean.saveViaCopy(this.to);
+ Assert.assertTrue(this.to.exists());
+ Assert.assertFalse(this.from.exists());
+ assertContents(this.to);
+ }
+
+ @Test(groups="fast")
+ public void testSaveOverExistingFile() throws Exception {
+ FileBean bean = new FileBean(from, "text/plain", "somefile.txt");
+
+ Assert.assertTrue(this.to.createNewFile());
+ bean.save(this.to);
+ Assert.assertTrue(this.to.exists());
+ Assert.assertFalse(this.from.exists());
+ assertContents(this.to);
+ }
+
+ @Test(groups="fast")
+ public void testSaveOverExistingFileWithContents() throws Exception {
+ FileBean bean = new FileBean(from, "text/plain", "somefile.txt");
+
+ Assert.assertTrue(this.to.createNewFile());
+ BufferedWriter out = new BufferedWriter(new FileWriter(this.to));
+ out.write("This is not what we should read back after the save!\n");
+ out.write("If we get this text back we're in trouble!\n");
+ out.close();
+
+ bean.save(this.to);
+ Assert.assertTrue(this.to.exists());
+ Assert.assertFalse(this.from.exists());
+ assertContents(this.to);
+ }
+
+ @Test(groups="fast")
+ public void testIntoDirectoryThatDoesNotExistYet() throws Exception {
+ FileBean bean = new FileBean(from, "text/plain", "somefile.txt");
+ this.to = new File(this.to, "somechild.txt");
+
+ bean.save(this.to);
+ Assert.assertTrue(this.to.exists());
+ Assert.assertFalse(this.from.exists());
+ assertContents(this.to);
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development