I am looking for an operation to move and overwrite a File. I know that there is a new Method in Java7, but I was hoping to get around Java7. Also I know about the Methods in FileUtils and Guava, but the FileUtils won't overwrite and the Guava one does not document it.
Also I am aware, I could write my own Method, well I started, but saw some Problems here and there, so I was hoping for something already done.
Do you have any suggestions?
I use the following method:
public static void rename(String oldFileName, String newFileName) {
new File(newFileName).delete();
File oldFile = new File(oldFileName);
oldFile.renameTo(new File(newFileName));
}
Apache FileUtils JavaDoc for FileUtils.copyFileToDirectory says, "If the destination file exists, then this method will overwrite it." After the copy, you could verify before deleting.
public boolean moveFile(File origfile, File destfile)
{
boolean fileMoved = false;
try{
FileUtils.copyFileToDirectory(origfile,new File(destfile.getParent()),true);
File newfile = new File(destfile.getParent() + File.separator + origfile.getName());
if(newfile.exists() && FileUtils.contentEqualsIgnoreCaseEOL(origfile,newfile,"UTF-8"))
{
origfile.delete();
fileMoved = true;
}
else
{
System.out.println("File fail to move successfully!");
}
}catch(Exception e){System.out.println(e);}
return fileMoved;
}
I am finished with writing my own Method, for everybody interested in a possible solution, I used the ApacheCommons FileUtils, also this is probably not perfect, but works well enough for me:
/**
* Will move the source File to the destination File.
* The Method will backup the dest File, copy source to
* dest, and then will delete the source and the backup.
*
* #param source
* File to be moved
* #param dest
* File to be overwritten (does not matter if
* non existent)
* #throws IOException
*/
public static void moveAndOverwrite(File source, File dest) throws IOException {
// Backup the src
File backup = CSVUtils.getNonExistingTempFile(dest);
FileUtils.copyFile(dest, backup);
FileUtils.copyFile(source, dest);
if (!source.delete()) {
throw new IOException("Failed to delete " + source.getName());
}
if (!backup.delete()) {
throw new IOException("Failed to delete " + backup.getName());
}
}
/**
* Recursive Method to generate a FileName in the same
* Folder as the {#code inputFile}, that is not existing
* and ends with {#code _temp}.
*
* #param inputFile
* The FileBase to generate a Tempfile
* #return A non existing File
*/
public static File getNonExistingTempFile(File inputFile) {
File tempFile = new File(inputFile.getParentFile(), inputFile.getName() + "_temp");
if (tempFile.exists()) {
return CSVUtils.getNonExistingTempFile(tempFile);
} else {
return tempFile;
}
}
A pure Java nio solution move with overriding method could be implemented with a pre-delete target as shown
public void move(File sourceFile, String targetFileName) {
Path sourcePath = sourceFile.toPath();
Path targetPath = Paths.get(targetFileName);
File file = targetFile.toFile();
if(file.isFile()){
Files.delete(targetPath);
}
Files.move(sourcePath, targetPath);
}
shortest solution which worked for me :
File destFile = new File(destDir, file.getName());
if(destFile.exists()) {
destFile.delete();
}
FileUtils.moveFileToDirectory(file, destDir, true);
Guava Files.write:
Overwrites a file with the contents of a byte array
Files.write(bytes, new File(path));
Files.copy:
Warning: If to represents an existing file, that file will be
overwritten with the contents of from. If to and from refer to the
same file, the contents of that file will be deleted.
Files.move uses copy under the hood. So its safe to assume it overwrites too.
In case you will proceed writing your own utility, you may want to take a look at implementation of the copy task in Ant since it supports overwriting.
Using Apache Commons FileUtils :
try {
FileUtils.moveFile(source, dest);
print("------------------------------");
print(name
+ " moved to "
+ PropertiesUtil
.getProperty(PropertiesUtil.COMPLETED_PATH));
} catch (FileExistsException fe){
if(dest.delete()){
try {
FileUtils.moveFile(source, dest);
} catch (IOException e) {
logger.error(e);
}
print("------------------------------");
print(name
+ " moved to "
+ PropertiesUtil
.getProperty(PropertiesUtil.COMPLETED_PATH));
}
} catch (Exception e) {
logger.error(e);
}
You could also use Tools Like https://xadisk.java.net/ to enable transactional access to existing file systems.
There is also an alternative from apache:
https://commons.apache.org/proper/commons-transaction/apidocs/org/apache/commons/transaction/file/FileResourceManager.html
Related
The condition is if the directory exists it has to create files in that specific directory without creating a new directory.
The below code only creates a file with the new directory but not for the existing directory . For example the directory name would be like "GETDIRECTION":
String PATH = "/remote/dir/server/";
String fileName = PATH.append(id).concat(getTimeStamp()).append(".txt");
String directoryName = PATH.append(this.getClassName());
File file = new File(String.valueOf(fileName));
File directory = new File(String.valueOf(directoryName));
if (!directory.exists()) {
directory.mkdir();
if (!file.exists() && !checkEnoughDiskSpace()) {
file.getParentFile().mkdir();
file.createNewFile();
}
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(value);
bw.close();
Java 8+ version:
Files.createDirectories(Paths.get("/Your/Path/Here"));
The Files.createDirectories() creates a new directory and parent directories that do not exist. This method does not throw an exception if the directory already exists.
This code checks for the existence of the directory first and creates it if not, and creates the file afterwards. Please note that I couldn't verify some of your method calls as I don't have your complete code, so I'm assuming the calls to things like getTimeStamp() and getClassName() will work. You should also do something with the possible IOException that can be thrown when using any of the java.io.* classes - either your function that writes the files should throw this exception (and it be handled elsewhere), or you should do it in the method directly. Also, I assumed that id is of type String - I don't know as your code doesn't explicitly define it. If it is something else like an int, you should probably cast it to a String before using it in the fileName as I have done here.
Also, I replaced your append calls with concat or + as I saw appropriate.
public void writeFile(String value){
String PATH = "/remote/dir/server/";
String directoryName = PATH.concat(this.getClassName());
String fileName = id + getTimeStamp() + ".txt";
File directory = new File(directoryName);
if (! directory.exists()){
directory.mkdir();
// If you require it to make the entire directory path including parents,
// use directory.mkdirs(); here instead.
}
File file = new File(directoryName + "/" + fileName);
try{
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(value);
bw.close();
}
catch (IOException e){
e.printStackTrace();
System.exit(-1);
}
}
You should probably not use bare path names like this if you want to run the code on Microsoft Windows - I'm not sure what it will do with the / in the filenames. For full portability, you should probably use something like File.separator to construct your paths.
Edit: According to a comment by JosefScript below, it's not necessary to test for directory existence. The directory.mkdir() call will return true if it created a directory, and false if it didn't, including the case when the directory already existed.
Trying to make this as short and simple as possible. Creates directory if it doesn't exist, and then returns the desired file:
/** Creates parent directories if necessary. Then returns file */
private static File fileWithDirectoryAssurance(String directory, String filename) {
File dir = new File(directory);
if (!dir.exists()) dir.mkdirs();
return new File(directory + "/" + filename);
}
I would suggest the following for Java8+.
/**
* Creates a File if the file does not exist, or returns a
* reference to the File if it already exists.
*/
public File createOrRetrieve(final String target) throws IOException {
final File answer;
Path path = Paths.get(target);
Path parent = path.getParent();
if(parent != null && Files.notExists(parent)) {
Files.createDirectories(path);
}
if(Files.notExists(path)) {
LOG.info("Target file \"" + target + "\" will be created.");
answer = Files.createFile(path).toFile();
} else {
LOG.info("Target file \"" + target + "\" will be retrieved.");
answer = path.toFile();
}
return answer;
}
Edit: Updated to fix bug as indicated by #Cataclysm and #Marcono1234. Thx guys:)
code:
// Create Directory if not exist then Copy a file.
public static void copyFile_Directory(String origin, String destDir, String destination) throws IOException {
Path FROM = Paths.get(origin);
Path TO = Paths.get(destination);
File directory = new File(String.valueOf(destDir));
if (!directory.exists()) {
directory.mkdir();
}
//overwrite the destination file if it exists, and copy
// the file attributes, including the rwx permissions
CopyOption[] options = new CopyOption[]{
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES
};
Files.copy(FROM, TO, options);
}
Simple Solution using using java.nio.Path
public static Path createFileWithDir(String directory, String filename) {
File dir = new File(directory);
if (!dir.exists()) dir.mkdirs();
return Paths.get(directory + File.separatorChar + filename);
}
If you create a web based application, the better solution is to check the directory exists or not then create the file if not exist. If exists, recreate again.
private File createFile(String path, String fileName) throws IOException {
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource(".").getFile() + path + fileName);
// Lets create the directory
try {
file.getParentFile().mkdir();
} catch (Exception err){
System.out.println("ERROR (Directory Create)" + err.getMessage());
}
// Lets create the file if we have credential
try {
file.createNewFile();
} catch (Exception err){
System.out.println("ERROR (File Create)" + err.getMessage());
}
return file;
}
A simple solution using Java 8
public void init(String multipartLocation) throws IOException {
File storageDirectory = new File(multipartLocation);
if (!storageDirectory.exists()) {
if (!storageDirectory.mkdir()) {
throw new IOException("Error creating directory.");
}
}
}
If you're using Java 8 or above, then Files.createDirectories() method works the best.
I'm writing a Java program which will execute an external file ~/Java/exampleProject/bin/import.sh. My program is under package gqqnbig. So the directory structure is
exampleProject/
bin/
import.sh
gqqnbig/
*.class
When I debug the program in eclipse, the working directory is ~/Java/exampleProject/. I have to execute bin/import.sh.
When I run the program in cmd, the current directory is ~/Java/exampleProject/bin, my code will not find import.sh.
The program has to be portable (distribute with import.sh). With the correct directory structure, it should work in my computer as well as in your computer, so I cannot hard code the path of import.sh.
I also want to pack it into a single jar file. The desired structure is (Figure 1)
bin/
import.sh
program.jar
So how can my program find import.sh when run in eclipse, cmd and jar?
UPDATE
I ask my question in another way. Please implement getAbsolutePath function, so that no matther the code is running in eclipse, in cmd, or as a jar file in a folder which also has import.sh (See Figure 1), the output is identical.
public static void main(String[] args)
{
System.out.println("Look for "+getAbsolutePath()+"\\import.sh");
}
Here's a method pulled from one of my projects. It get's the folder that the jar file is located in as opposed to the directory if was run from if invoked on the command line.
/**
* Retrieve a File representation of the folder this application is
* located in.
*
* #return
*/
private static File getApplicationRootFolder()
{
String path = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(path, "UTF-8");
File jarParentFolder = new File(decodedPath).getParentFile();
if (jarParentFolder.exists() && jarParentFolder.canRead()
{
File shellScript = new File(jarParentFolder, "import.sh")
}
catch (UnsupportedEncodingException e)
{
Main.myLog.error(TAG, "Unencoding jar path failed on:\n\t" + path);
e.printStackTrace();
return null;
}
}
You can then use that directory to make a File object for your shell script File shellScript = new File(getApplicationRootFolder(), scriptFilename);
EDIT: Follow up questions to try to help you out and a solution
So you want to be able to access one file that has three locations depending on when/where you code is run. This is how I see those cases:
Case 1: Running directly from Eclipse (unpackaged code):
shell script: X:/Java/exampleProject/bin/import.sh
class file: X:/Java/exampleProject/bin/gqqnbig/YourClass.class
Case 2: Running the packaged jar (shell script inside):
shell script: X:/Java/YourJar.jar/bin/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
Case 3: Running the packaged jar (shell script external):
shell script: X:/Java/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
What I think you need to do is prioritise the order you look at these locations and fall back to the next one in line if the shell script isn't found. I'd guess you want:
1. external to jar
2. inside packaged jar
3. unpackaged
So to access these you will need to write each separately and move through each until you get File.exists() == true.
Something like what follows. Note I didn't test this and there are likely errors. I'll leave you to sort them out. My code is based on the assumptions made above, again I'll leave you to modify the code based on any incorrect guesses.
So here's a class with one public method taking a filename argument and returning an InputStream. I opted for InputStream in all cases as once you package up your jar you cannot access the resources as File objects any more, only Streams.
public class FileGetter
{
private static String RESOURCE_DIRECTORY = "bin";
/**
* Retrieve an InputStream for a resource file.
*
* #param filename
* #return
*/
public InputStream getResourceFileStream(String filename)
{
// this is where you decide your preference or the priority of the locations
InputStream inputStream = null;
inputStream = getExternalFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalPackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalUnpackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
// couldn't find the file anywhere so log some error or throw an exception
return null;
}
/**
* Retrieve an InputStream for a file located outside your Jar
*
* #param filename
* #return
*/
private static InputStream getExternalFile(String filename)
{
// get the jar's absolute location on disk (regardless of current 'working directory')
String appRootPath = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(appRootPath, "UTF-8");
File jarfile = new File(decodedPath);
File parentDirectory = jarfile.getParentFile();
if (testExists(parentDirectory))
{
File shellScript = new File(parentDirectory, filename);
if (testExists(shellScript))
{
return new FileInputStream(shellScript);
}
}
}
catch (UnsupportedEncodingException e)
{}
catch (NullPointerException e)
{}
catch (FileNotFoundException e)
{}
// if any part fails return null
return null;
}
/**
* Retrieve an InputStream for a file located inside your Jar.
*
* #param filename
* #return
*/
private static InputStream getInternalPackagedFile(String filename)
{
// root directory is defined as the jar's root so we start with a "/".
URL resUrl = FileGetter.class.getResource(File.separator + RESOURCE_DIRECTORY
+ File.separator + filename);
String badPath = resUrl.getPath();
String goodPath = badPath.substring(badPath.indexOf("!") + 1);
InputStream input = FileGetter.class.getResourceAsStream(goodPath);
// returns null if nothing there so just
return input;
}
private static InputStream getInternalUnpackagedFile(String filename)
{
// eclipse will 'cd' to the code's directory so we use relative paths
File shellScriptFile = new File(RESOURCE_DIRECTORY + File.separator + filename);
if (testExists(shellScriptFile))
{
try
{
InputStream shellScriptStream = new FileInputStream(shellScriptFile);
if (shellScriptStream != null)
{
return shellScriptStream;
}
}
catch (FileNotFoundException e)
{}
}
return null;
}
/**
* Test that a file exists and can be read.
*
* #param file
* #return
*/
private static boolean testExists(File file)
{
return file != null && file.exists() && file.canRead();
}
}
But with all that being said a better way to sort this would be to ensure that the file exists on disk and create it if not found. Then execute the script from disk.
I would like to know a definitive answer for this myself.
As a workaround I would put 'import.sh' inside the exampleProject and change the relative path to 'import.sh'.
In theory that should work inside Eclipse, and as a packaged Jar file with program.jar and import.sh in the same directory.
It won't work on the cmd prompt unfortunately, maybe someone can suggest a better method.
-Kaz
I composed a solution. Call getExecutablePath() to get unified path.
public static File getExecutablePath()
{
String workingDirectory = System.getProperty("user.dir");
File binFile = new File(workingDirectory, "bin");
if (binFile.exists() && (new File(workingDirectory, "src")).exists())
{
return binFile;
}
else if (isRunningFromJar())
return getApplicationRootFolder();
else
return new File(workingDirectory);
}
public static boolean isRunningFromJar()
{
String className = SystemHelper.class.getName().replace('.', '/');
String classJar = SystemHelper.class.getResource("/" + className + ".class").toString();
return classJar.startsWith("jar:");
}
/**
* Retrieve a File representation of the folder this application is located in.
*
* #return
*/
private static File getApplicationRootFolder()
{
try
{
String path = SystemHelper.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath;
decodedPath = URLDecoder.decode(path, "UTF-8");
File jarfile = new File(decodedPath);
return jarfile.getParentFile();
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
I want to copy a file from a jar. The file that I am copying is going to be copied outside the working directory. I have done some tests and all methods I try end up with 0 byte files.
EDIT: I want the copying of the file to be done via a program, not manually.
First of all I want to say that some answers posted before are entirely correct, but I want to give mine, since sometimes we can't use open source libraries under the GPL, or because we are too lazy to download the jar XD or what ever your reason is here is a standalone solution.
The function below copy the resource beside the Jar file:
/**
* Export a resource embedded into a Jar file to the local file path.
*
* #param resourceName ie.: "/SmartLibrary.dll"
* #return The path to the exported resource
* #throws Exception
*/
static public String ExportResource(String resourceName) throws Exception {
InputStream stream = null;
OutputStream resStreamOut = null;
String jarFolder;
try {
stream = ExecutingClass.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
if(stream == null) {
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}
int readBytes;
byte[] buffer = new byte[4096];
jarFolder = new File(ExecutingClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/');
resStreamOut = new FileOutputStream(jarFolder + resourceName);
while ((readBytes = stream.read(buffer)) > 0) {
resStreamOut.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
throw ex;
} finally {
stream.close();
resStreamOut.close();
}
return jarFolder + resourceName;
}
Just change ExecutingClass to the name of your class, and call it like this:
String fullPath = ExportResource("/myresource.ext");
Edit for Java 7+ (for your convenience)
As answered by GOXR3PLUS and noted by Andy Thomas you can achieve this with:
Files.copy( InputStream in, Path target, CopyOption... options)
See GOXR3PLUS answer for more details
Given your comment about 0-byte files, I have to assume you're trying to do this programmatically, and, given your tags, that you're doing it in Java. If that's true, then just use Class.getResource() to get a URL pointing to the file in your JAR, then Apache Commons IO FileUtils.copyURLToFile() to copy it out to the file system. E.g.:
URL inputUrl = getClass().getResource("/absolute/path/of/source/in/jar/file");
File dest = new File("/path/to/destination/file");
FileUtils.copyURLToFile(inputUrl, dest);
Most likely, the problem with whatever code you have now is that you're (correctly) using a buffered output stream to write to the file but (incorrectly) failing to close it.
Oh, and you should edit your question to clarify exactly how you want to do this (programmatically, not, language, ...)
Faster way to do it with Java 7+ , plus code to get the current directory:
/**
* Copy a file from source to destination.
*
* #param source
* the source
* #param destination
* the destination
* #return True if succeeded , False if not
*/
public static boolean copy(InputStream source , String destination) {
boolean succeess = true;
System.out.println("Copying ->" + source + "\n\tto ->" + destination);
try {
Files.copy(source, Paths.get(destination), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
logger.log(Level.WARNING, "", ex);
succeess = false;
}
return succeess;
}
Testing it (icon.png is an image inside the package image of the application):
copy(getClass().getResourceAsStream("/image/icon.png"),getBasePathForClass(Main.class)+"icon.png");
About the line of code (getBasePathForClass(Main.class)): -> check the answer i have added here :) -> Getting the Current Working Directory in Java
Java 8 (actually FileSystem is there since 1.7) comes with some cool new classes/methods to deal with this. As somebody already mentioned that JAR is basically ZIP file, you could use
final URI jarFileUril = URI.create("jar:file:" + file.toURI().getPath());
final FileSystem fs = FileSystems.newFileSystem(jarFileUri, env);
(See Zip File)
Then you can use one of the convenient methods like:
fs.getPath("filename");
Then you can use Files class
try (final Stream<Path> sources = Files.walk(from)) {
sources.forEach(src -> {
final Path dest = to.resolve(from.relativize(src).toString());
try {
if (Files.isDirectory(from)) {
if (Files.notExists(to)) {
log.trace("Creating directory {}", to);
Files.createDirectories(to);
}
} else {
log.trace("Extracting file {} to {}", from, to);
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException("Failed to unzip file.", e);
}
});
}
Note: I tried that to unpack JAR files for testing
Robust solution:
public static void copyResource(String res, String dest, Class c) throws IOException {
InputStream src = c.getResourceAsStream(res);
Files.copy(src, Paths.get(dest), StandardCopyOption.REPLACE_EXISTING);
}
You can use it like this:
File tempFileGdalZip = File.createTempFile("temp_gdal", ".zip");
copyResource("/gdal.zip", tempFileGdalZip.getAbsolutePath(), this.getClass());
Use the JarInputStream class:
// assuming you already have an InputStream to the jar file..
JarInputStream jis = new JarInputStream( is );
// get the first entry
JarEntry entry = jis.getNextEntry();
// we will loop through all the entries in the jar file
while ( entry != null ) {
// test the entry.getName() against whatever you are looking for, etc
if ( matches ) {
// read from the JarInputStream until the read method returns -1
// ...
// do what ever you want with the read output
// ...
// if you only care about one file, break here
}
// get the next entry
entry = jis.getNextEntry();
}
jis.close();
See also: JarEntry
To copy a file from your jar, to the outside, you need to use the following approach:
Get a InputStream to a the file inside your jar file using getResourceAsStream()
We open our target file using a FileOutputStream
We copy bytes from the input to the output stream
We close our streams to prevent resource leaks
Example code that also contains a variable to not replace the existing values:
public File saveResource(String name) throws IOException {
return saveResource(name, true);
}
public File saveResource(String name, boolean replace) throws IOException {
return saveResource(new File("."), name, replace)
}
public File saveResource(File outputDirectory, String name) throws IOException {
return saveResource(outputDirectory, name, true);
}
public File saveResource(File outputDirectory, String name, boolean replace)
throws IOException {
File out = new File(outputDirectory, name);
if (!replace && out.exists())
return out;
// Step 1:
InputStream resource = this.getClass().getResourceAsStream(name);
if (resource == null)
throw new FileNotFoundException(name + " (resource not found)");
// Step 2 and automatic step 4
try(InputStream in = resource;
OutputStream writer = new BufferedOutputStream(
new FileOutputStream(out))) {
// Step 3
byte[] buffer = new byte[1024 * 4];
int length;
while((length = in.read(buffer)) >= 0) {
writer.write(buffer, 0, length);
}
}
return out;
}
A jar is just a zip file. Unzip it (using whatever method you're comfortable with) and copy the file normally.
${JAVA_HOME}/bin/jar -cvf /path/to.jar
Is there a standard and reliable way of creating a temporary directory inside a Java application? There's an entry in Java's issue database, which has a bit of code in the comments, but I wonder if there is a standard solution to be found in one of the usual libraries (Apache Commons etc.) ?
If you are using JDK 7 use the new Files.createTempDirectory class to create the temporary directory.
Path tempDirWithPrefix = Files.createTempDirectory(prefix);
Before JDK 7 this should do it:
public static File createTempDirectory()
throws IOException
{
final File temp;
temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
if(!(temp.delete()))
{
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if(!(temp.mkdir()))
{
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return (temp);
}
You could make better exceptions (subclass IOException) if you want.
The Google Guava library has a ton of helpful utilities. One of note here is the Files class. It has a bunch of useful methods including:
File myTempDir = Files.createTempDir();
This does exactly what you asked for in one line. If you read the documentation here you'll see that the proposed adaptation of File.createTempFile("install", "dir") typically introduces security vulnerabilities.
If you need a temporary directory for testing and you are using jUnit, #Rule together with TemporaryFolder solves your problem:
#Rule
public TemporaryFolder folder = new TemporaryFolder();
From the documentation:
The TemporaryFolder Rule allows creation of files and folders that are guaranteed to be deleted when the test method finishes (whether it passes or fails)
Update:
If you are using JUnit Jupiter (version 5.1.1 or greater), you have the option to use JUnit Pioneer which is the JUnit 5 Extension Pack.
Copied from the project documentation:
For example, the following test registers the extension for a single test method, creates and writes a file to the temporary directory and checks its content.
#Test
#ExtendWith(TempDirectory.class)
void test(#TempDir Path tempDir) {
Path file = tempDir.resolve("test.txt");
writeFile(file);
assertExpectedFileContent(file);
}
More info in the JavaDoc and the JavaDoc of TempDirectory
Gradle:
dependencies {
testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}
Maven:
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.1.2</version>
<scope>test</scope>
</dependency>
Update 2:
The #TempDir annotation was added to the JUnit Jupiter 5.4.0 release as an experimental feature. Example copied from the JUnit 5 User Guide:
#Test
void writeItemsToFile(#TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}
Naively written code to solve this problem suffers from race conditions, including several of the answers here. Historically you could think carefully about race conditions and write it yourself, or you could use a third-party library like Google's Guava (as Spina's answer suggested.) Or you could write buggy code.
But as of JDK 7, there is good news! The Java standard library itself now provides a properly working (non-racy) solution to this problem. You want java.nio.file.Files#createTempDirectory(). From the documentation:
public static Path createTempDirectory(Path dir,
String prefix,
FileAttribute<?>... attrs)
throws IOException
Creates a new directory in the specified directory, using the given prefix to generate its name. The resulting Path is associated with the same FileSystem as the given directory.
The details as to how the name of the directory is constructed is implementation dependent and therefore not specified. Where possible the prefix is used to construct candidate names.
This effectively resolves the embarrassingly ancient bug report in the Sun bug tracker which asked for just such a function.
This is the source code to the Guava library's Files.createTempDir(). It's nowhere as complex as you might think:
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}
By default:
private static final int TEMP_DIR_ATTEMPTS = 10000;
See here
Do not use deleteOnExit() even if you explicitly delete it later.
Google 'deleteonexit is evil' for more info, but the gist of the problem is:
deleteOnExit() only deletes for normal JVM shutdowns, not crashes or killing the JVM process.
deleteOnExit() only deletes on JVM shutdown - not good for long running server processes because:
The most evil of all - deleteOnExit() consumes memory for each temp file entry. If your process is running for months, or creates a lot of temp files in a short time, you consume memory and never release it until the JVM shuts down.
As of Java 1.7 createTempDirectory(prefix, attrs) and createTempDirectory(dir, prefix, attrs) are included in java.nio.file.Files
Example:
File tempDir = Files.createTempDirectory("foobar").toFile();
This is what I decided to do for my own code:
/**
* Create a new temporary directory. Use something like
* {#link #recursiveDelete(File)} to clean this directory up since it isn't
* deleted automatically
* #return the new directory
* #throws IOException if there is an error creating the temporary directory
*/
public static File createTempDir() throws IOException
{
final File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
File newTempDir;
final int maxAttempts = 9;
int attemptCount = 0;
do
{
attemptCount++;
if(attemptCount > maxAttempts)
{
throw new IOException(
"The highly improbable has occurred! Failed to " +
"create a unique temporary directory after " +
maxAttempts + " attempts.");
}
String dirName = UUID.randomUUID().toString();
newTempDir = new File(sysTempDir, dirName);
} while(newTempDir.exists());
if(newTempDir.mkdirs())
{
return newTempDir;
}
else
{
throw new IOException(
"Failed to create temp dir named " +
newTempDir.getAbsolutePath());
}
}
/**
* Recursively delete file or directory
* #param fileOrDir
* the file or dir to delete
* #return
* true iff all files are successfully deleted
*/
public static boolean recursiveDelete(File fileOrDir)
{
if(fileOrDir.isDirectory())
{
// recursively delete contents
for(File innerFile: fileOrDir.listFiles())
{
if(!FileUtilities.recursiveDelete(innerFile))
{
return false;
}
}
}
return fileOrDir.delete();
}
Well, "createTempFile" actually creates the file. So why not just delete it first, and then do the mkdir on it?
This code should work reasonably well:
public static File createTempDir() {
final String baseTempPath = System.getProperty("java.io.tmpdir");
Random rand = new Random();
int randomInt = 1 + rand.nextInt();
File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt);
if (tempDir.exists() == false) {
tempDir.mkdir();
}
tempDir.deleteOnExit();
return tempDir;
}
As discussed in this RFE and its comments, you could call tempDir.delete() first. Or you could use System.getProperty("java.io.tmpdir") and create a directory there. Either way, you should remember to call tempDir.deleteOnExit(), or the file won't be deleted after you're done.
Just for completion, this is the code from google guava library. It is not my code, but I think it is valueable to show it here in this thread.
/** Maximum loop count when creating temp directories. */
private static final int TEMP_DIR_ATTEMPTS = 10000;
/**
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
* defined by the {#code java.io.tmpdir} system property), and returns its name.
*
* <p>Use this method instead of {#link File#createTempFile(String, String)} when you wish to
* create a directory, not a regular file. A common pitfall is to call {#code createTempFile},
* delete the file and create a directory in its place, but this leads a race condition which can
* be exploited to create security vulnerabilities, especially when executable files are to be
* written into the directory.
*
* <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second.
*
* #return the newly-created directory
* #throws IllegalStateException if the directory could not be created
*/
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException(
"Failed to create directory within "
+ TEMP_DIR_ATTEMPTS
+ " attempts (tried "
+ baseName
+ "0 to "
+ baseName
+ (TEMP_DIR_ATTEMPTS - 1)
+ ')');
}
I got the same problem so this is just another answer for those who are interested, and it's similar to one of the above:
public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime();
static {
File f = new File(tempDir);
if(!f.exists())
f.mkdir();
}
And for my application, I decided that to add in a option to clear the temp on exit so I added in a shut-down hook:
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
//stackless deletion
String root = MainWindow.tempDir;
Stack<String> dirStack = new Stack<String>();
dirStack.push(root);
while(!dirStack.empty()) {
String dir = dirStack.pop();
File f = new File(dir);
if(f.listFiles().length==0)
f.delete();
else {
dirStack.push(dir);
for(File ff: f.listFiles()) {
if(ff.isFile())
ff.delete();
else if(ff.isDirectory())
dirStack.push(ff.getPath());
}
}
}
}
});
The method delete all subdirs and files before deleting the temp, without using the callstack (which is totally optional and you could do it with recursion at this point), but I want to be on the safe side.
As you can see in the other answers, no standard approach has arisen.
Hence you already mentioned Apache Commons, I propose the following approach using FileUtils from Apache Commons IO:
/**
* Creates a temporary subdirectory in the standard temporary directory.
* This will be automatically deleted upon exit.
*
* #param prefix
* the prefix used to create the directory, completed by a
* current timestamp. Use for instance your application's name
* #return the directory
*/
public static File createTempDirectory(String prefix) {
final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath()
+ "/" + prefix + System.currentTimeMillis());
tmp.mkdir();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
try {
FileUtils.deleteDirectory(tmp);
} catch (IOException e) {
e.printStackTrace();
}
}
});
return tmp;
}
This is preferred since apache commons the library that comes as closest to the asked "standard" and works with both JDK 7 and older versions. This also returns an "old" File instance (which is stream based) and not a "new" Path instance (which is buffer based and would be the result of JDK7's getTemporaryDirectory() method) -> Therefore it returns what most people need when they want to create a temporary directory.
Try this small example:
Code:
try {
Path tmpDir = Files.createTempDirectory("tmpDir");
System.out.println(tmpDir.toString());
Files.delete(tmpDir);
} catch (IOException e) {
e.printStackTrace();
}
Imports:
java.io.IOException
java.nio.file.Files
java.nio.file.Path
Console output on Windows machine:
C:\Users\userName\AppData\Local\Temp\tmpDir2908538301081367877
Comment:
Files.createTempDirectory generates unique ID atomatically - 2908538301081367877.
Note:
Read the following for deleting directories recursively:
Delete directories recursively in Java
I like the multiple attempts at creating a unique name but even this solution does not rule out a race condition. Another process can slip in after the test for exists() and the if(newTempDir.mkdirs()) method invocation. I have no idea how to completely make this safe without resorting to native code, which I presume is what's buried inside File.createTempFile().
Before Java 7 you could also:
File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();
Using File#createTempFile and delete to create a unique name for the directory seems ok. You should add a ShutdownHook to delete the directory (recursively) on JVM shutdown.
Is there a standard and reliable way of creating a temporary directory inside a Java application? There's an entry in Java's issue database, which has a bit of code in the comments, but I wonder if there is a standard solution to be found in one of the usual libraries (Apache Commons etc.) ?
If you are using JDK 7 use the new Files.createTempDirectory class to create the temporary directory.
Path tempDirWithPrefix = Files.createTempDirectory(prefix);
Before JDK 7 this should do it:
public static File createTempDirectory()
throws IOException
{
final File temp;
temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
if(!(temp.delete()))
{
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if(!(temp.mkdir()))
{
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return (temp);
}
You could make better exceptions (subclass IOException) if you want.
The Google Guava library has a ton of helpful utilities. One of note here is the Files class. It has a bunch of useful methods including:
File myTempDir = Files.createTempDir();
This does exactly what you asked for in one line. If you read the documentation here you'll see that the proposed adaptation of File.createTempFile("install", "dir") typically introduces security vulnerabilities.
If you need a temporary directory for testing and you are using jUnit, #Rule together with TemporaryFolder solves your problem:
#Rule
public TemporaryFolder folder = new TemporaryFolder();
From the documentation:
The TemporaryFolder Rule allows creation of files and folders that are guaranteed to be deleted when the test method finishes (whether it passes or fails)
Update:
If you are using JUnit Jupiter (version 5.1.1 or greater), you have the option to use JUnit Pioneer which is the JUnit 5 Extension Pack.
Copied from the project documentation:
For example, the following test registers the extension for a single test method, creates and writes a file to the temporary directory and checks its content.
#Test
#ExtendWith(TempDirectory.class)
void test(#TempDir Path tempDir) {
Path file = tempDir.resolve("test.txt");
writeFile(file);
assertExpectedFileContent(file);
}
More info in the JavaDoc and the JavaDoc of TempDirectory
Gradle:
dependencies {
testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}
Maven:
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.1.2</version>
<scope>test</scope>
</dependency>
Update 2:
The #TempDir annotation was added to the JUnit Jupiter 5.4.0 release as an experimental feature. Example copied from the JUnit 5 User Guide:
#Test
void writeItemsToFile(#TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}
Naively written code to solve this problem suffers from race conditions, including several of the answers here. Historically you could think carefully about race conditions and write it yourself, or you could use a third-party library like Google's Guava (as Spina's answer suggested.) Or you could write buggy code.
But as of JDK 7, there is good news! The Java standard library itself now provides a properly working (non-racy) solution to this problem. You want java.nio.file.Files#createTempDirectory(). From the documentation:
public static Path createTempDirectory(Path dir,
String prefix,
FileAttribute<?>... attrs)
throws IOException
Creates a new directory in the specified directory, using the given prefix to generate its name. The resulting Path is associated with the same FileSystem as the given directory.
The details as to how the name of the directory is constructed is implementation dependent and therefore not specified. Where possible the prefix is used to construct candidate names.
This effectively resolves the embarrassingly ancient bug report in the Sun bug tracker which asked for just such a function.
This is the source code to the Guava library's Files.createTempDir(). It's nowhere as complex as you might think:
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}
By default:
private static final int TEMP_DIR_ATTEMPTS = 10000;
See here
Do not use deleteOnExit() even if you explicitly delete it later.
Google 'deleteonexit is evil' for more info, but the gist of the problem is:
deleteOnExit() only deletes for normal JVM shutdowns, not crashes or killing the JVM process.
deleteOnExit() only deletes on JVM shutdown - not good for long running server processes because:
The most evil of all - deleteOnExit() consumes memory for each temp file entry. If your process is running for months, or creates a lot of temp files in a short time, you consume memory and never release it until the JVM shuts down.
As of Java 1.7 createTempDirectory(prefix, attrs) and createTempDirectory(dir, prefix, attrs) are included in java.nio.file.Files
Example:
File tempDir = Files.createTempDirectory("foobar").toFile();
This is what I decided to do for my own code:
/**
* Create a new temporary directory. Use something like
* {#link #recursiveDelete(File)} to clean this directory up since it isn't
* deleted automatically
* #return the new directory
* #throws IOException if there is an error creating the temporary directory
*/
public static File createTempDir() throws IOException
{
final File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
File newTempDir;
final int maxAttempts = 9;
int attemptCount = 0;
do
{
attemptCount++;
if(attemptCount > maxAttempts)
{
throw new IOException(
"The highly improbable has occurred! Failed to " +
"create a unique temporary directory after " +
maxAttempts + " attempts.");
}
String dirName = UUID.randomUUID().toString();
newTempDir = new File(sysTempDir, dirName);
} while(newTempDir.exists());
if(newTempDir.mkdirs())
{
return newTempDir;
}
else
{
throw new IOException(
"Failed to create temp dir named " +
newTempDir.getAbsolutePath());
}
}
/**
* Recursively delete file or directory
* #param fileOrDir
* the file or dir to delete
* #return
* true iff all files are successfully deleted
*/
public static boolean recursiveDelete(File fileOrDir)
{
if(fileOrDir.isDirectory())
{
// recursively delete contents
for(File innerFile: fileOrDir.listFiles())
{
if(!FileUtilities.recursiveDelete(innerFile))
{
return false;
}
}
}
return fileOrDir.delete();
}
Well, "createTempFile" actually creates the file. So why not just delete it first, and then do the mkdir on it?
This code should work reasonably well:
public static File createTempDir() {
final String baseTempPath = System.getProperty("java.io.tmpdir");
Random rand = new Random();
int randomInt = 1 + rand.nextInt();
File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt);
if (tempDir.exists() == false) {
tempDir.mkdir();
}
tempDir.deleteOnExit();
return tempDir;
}
As discussed in this RFE and its comments, you could call tempDir.delete() first. Or you could use System.getProperty("java.io.tmpdir") and create a directory there. Either way, you should remember to call tempDir.deleteOnExit(), or the file won't be deleted after you're done.
Just for completion, this is the code from google guava library. It is not my code, but I think it is valueable to show it here in this thread.
/** Maximum loop count when creating temp directories. */
private static final int TEMP_DIR_ATTEMPTS = 10000;
/**
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
* defined by the {#code java.io.tmpdir} system property), and returns its name.
*
* <p>Use this method instead of {#link File#createTempFile(String, String)} when you wish to
* create a directory, not a regular file. A common pitfall is to call {#code createTempFile},
* delete the file and create a directory in its place, but this leads a race condition which can
* be exploited to create security vulnerabilities, especially when executable files are to be
* written into the directory.
*
* <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second.
*
* #return the newly-created directory
* #throws IllegalStateException if the directory could not be created
*/
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException(
"Failed to create directory within "
+ TEMP_DIR_ATTEMPTS
+ " attempts (tried "
+ baseName
+ "0 to "
+ baseName
+ (TEMP_DIR_ATTEMPTS - 1)
+ ')');
}
I got the same problem so this is just another answer for those who are interested, and it's similar to one of the above:
public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime();
static {
File f = new File(tempDir);
if(!f.exists())
f.mkdir();
}
And for my application, I decided that to add in a option to clear the temp on exit so I added in a shut-down hook:
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
//stackless deletion
String root = MainWindow.tempDir;
Stack<String> dirStack = new Stack<String>();
dirStack.push(root);
while(!dirStack.empty()) {
String dir = dirStack.pop();
File f = new File(dir);
if(f.listFiles().length==0)
f.delete();
else {
dirStack.push(dir);
for(File ff: f.listFiles()) {
if(ff.isFile())
ff.delete();
else if(ff.isDirectory())
dirStack.push(ff.getPath());
}
}
}
}
});
The method delete all subdirs and files before deleting the temp, without using the callstack (which is totally optional and you could do it with recursion at this point), but I want to be on the safe side.
As you can see in the other answers, no standard approach has arisen.
Hence you already mentioned Apache Commons, I propose the following approach using FileUtils from Apache Commons IO:
/**
* Creates a temporary subdirectory in the standard temporary directory.
* This will be automatically deleted upon exit.
*
* #param prefix
* the prefix used to create the directory, completed by a
* current timestamp. Use for instance your application's name
* #return the directory
*/
public static File createTempDirectory(String prefix) {
final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath()
+ "/" + prefix + System.currentTimeMillis());
tmp.mkdir();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
try {
FileUtils.deleteDirectory(tmp);
} catch (IOException e) {
e.printStackTrace();
}
}
});
return tmp;
}
This is preferred since apache commons the library that comes as closest to the asked "standard" and works with both JDK 7 and older versions. This also returns an "old" File instance (which is stream based) and not a "new" Path instance (which is buffer based and would be the result of JDK7's getTemporaryDirectory() method) -> Therefore it returns what most people need when they want to create a temporary directory.
Try this small example:
Code:
try {
Path tmpDir = Files.createTempDirectory("tmpDir");
System.out.println(tmpDir.toString());
Files.delete(tmpDir);
} catch (IOException e) {
e.printStackTrace();
}
Imports:
java.io.IOException
java.nio.file.Files
java.nio.file.Path
Console output on Windows machine:
C:\Users\userName\AppData\Local\Temp\tmpDir2908538301081367877
Comment:
Files.createTempDirectory generates unique ID atomatically - 2908538301081367877.
Note:
Read the following for deleting directories recursively:
Delete directories recursively in Java
I like the multiple attempts at creating a unique name but even this solution does not rule out a race condition. Another process can slip in after the test for exists() and the if(newTempDir.mkdirs()) method invocation. I have no idea how to completely make this safe without resorting to native code, which I presume is what's buried inside File.createTempFile().
Before Java 7 you could also:
File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();
Using File#createTempFile and delete to create a unique name for the directory seems ok. You should add a ShutdownHook to delete the directory (recursively) on JVM shutdown.