Java: renameTo() function failing - java

Java
The code below was written to read all files in, and send the data to another method (setOutput()), and then call a method to rename the last read file to another directory, and then delete the original. Everything seems to work up until the smdrCleanup() method is called. The renameTo() is failing.
From what I understand, if a FileReader is wrapped in a BufferedReader, I only need to call BufferedReader.close() to release the last read file... which I am doing here.
I have also seen where if the file were still "open", being scanned by anti-virus programs, or otherwise locked by a process, the renameTo() function would fail. I have used Process Explorer to review what may have it locked, and I don't see anything locking it.
I have my method setup to throw any kind of IOExceptions, but I am not getting any exceptions. Everything runs, but the console merely says that the file was not copied.
I am running Eclipse Helios Release 2, Windows 7 Ultimate, local administrator, UAC disabled.
Any help would be greatly appreciated.
public void smdrReader(String path, String oldPath) throws IOException
{
output = null; //nullify the value of output to avoid duplicate data
File folder = new File(path); //setting the directory for raw data files
File[] listOfFiles = folder.listFiles(); //array of files within the folder/directory
//For loop to iterate through the available files, open, & read contents to String Buffer.
for (int i = 0; i < listOfFiles.length; i++)
{
if (listOfFiles[i].isFile()) //verifying next entry in array is a file
{
File fileName = new File(listOfFiles[i].getName());//getting file name from array iteration
StringBuffer fileData = new StringBuffer(2048);//establishing StringBuffer for reading file contents into
BufferedReader reader = new BufferedReader(new FileReader(path + fileName));//reader to actually access/read file
String readData = String.valueOf(reader);//String variable being set to value of the reader
fileData.append(readData);//appending data from String variable into StringBuffer variable
setOutput(fileData);//setting the value of "output" to the value of StringBuffer
fileData.delete(0, i);
reader.close();//closing the reader (closing the file)
smdrCleanup(oldPath,fileName.toString());//calling method to move processed file and delete original
}
}
}
//method to rename input file into another directory and delete the original file to avoid duplicate processing
public void smdrCleanup(String oldPathPassed, String fileNamePassed) throws IOException
{
File oldFile = new File(oldPathPassed);//establishing File object for path to processed folder
File fileName = new File(fileNamePassed);//establishing File object for the fileName to rename/delete
String oldFilePath = oldFile.toString();
boolean success = fileName.renameTo(new File(oldFilePath + "\\" + fileName + ".old"));//attempting to rename file
if (!success) //checking the success of the file rename operation
{
System.out.println("The File Was NOT Successfully Moved!");//reporting error if the file cannot be renamed
}
else
{
fileName.delete();//deleting the file if it was successfully renamed
}
}

oldFile.toString(); returns the full path of the file including its file name, so if your old file path is c:\path\to\file.txt, oldFilePath + "\\" + fileName + ".old" will be c:\path\to\file.txt\file.txt.old.
Since there is no folder c:\path\to\file.txt, it fails. change it to
boolean success = fileName.renameTo(new File(oldFilePath + ".old"));
And you should be good to go.

File.renameTo can fail for any number of reasons:
Many aspects of the behavior of this
method are inherently
platform-dependent: The rename
operation might not be able to move a
file from one filesystem to another,
it might not be atomic, and it might
not succeed if a file with the
destination abstract pathname already
exists. The return value should always
be checked to make sure that the
rename operation was successful.
But there's not much feedback on why it fails. Before calling renameTo, verify that the file you're moving exists, and the parent directory you are moving it to also exists, and canWrite(). Are these on the same disk volume? If not, it might fail.
*EDIT: Code sample added *
Try something like the following. Modifications:
Accepts File objects instead of Strings
Uses 2-arg File constructor to create a child File object in a parent directory
Better error checking
This might give you some clues into what is failing.
public void smdrCleanup(File oldPathPassed, File fileNamePassed) throws IOException {
if (!oldPathPassed.exists() || !oldPathPassed.isDirectory() || !oldPathPassed.canWrite() ) throw new IOException("Dest is not a writable directory: " + oldPathPassed);
if (!fileNamePassed.exists()) throw new IOException("File does not exist: " + fileNamePassed);
final File dest = new File(oldPathPassed, fileNamePassed + ".old");
if (dest.exists()) throw new IOException("File already exists: " + dest);
boolean success = (fileNamePassed).renameTo(dest);//attempting to rename file
if (!success) //checking the success of the file rename operation
{
throw new IOException("The File Was NOT Successfully Moved!");
} else {
// file was successfully renamed, no need to delete
}
}

Related

Provide log if the two files are identical and has same contents in Java

I have below code where i am reading the file from particular directory, processing it and once processed i am moving the file to archive directory. This is working fine. I am receiving new file everyday and i am using Control-M scheduler job to run this process.
Now in next run i am reading the new file from that particularly directory again and checking this file with the file in the archive directory and if the content is different then only process the file else dont do anything. There is shell script written to do this job and we dont see any log for this process.
Now i want to produce log message in my java code if the files are identical from the particular directory and in the archive directory then generate log that 'files are identical'. But i dont know exactly how to do this. I dont want to write the the logic to process or move anything in the file ..i just need to check the files are equal and if it is then
produce log message. The file which i recieve are not very big and the max size can be till 10MB.
Below is my code:
for(Path inputFile : pathsToProcess) {
// read in the file:
readFile(inputFile.toAbsolutePath().toString());
// move the file away into the archive:
Path archiveDir = Paths.get(applicationContext.getEnvironment().getProperty(".archive.dir"));
Files.move(inputFile, archiveDir.resolve(inputFile.getFileName()),StandardCopyOption.REPLACE_EXISTING);
}
return true;
}
private void readFile(String inputFile) throws IOException, FileNotFoundException {
log.info("Import " + inputFile);
try (InputStream is = new FileInputStream(inputFile);
Reader underlyingReader = inputFile.endsWith("gz")
? new InputStreamReader(new GZIPInputStream(is), DEFAULT_CHARSET)
: new InputStreamReader(is, DEFAULT_CHARSET);
BufferedReader reader = new BufferedReader(underlyingReader)) {
if (isPxFile(inputFile)) {
Importer.processField(reader, tablenameFromFilename(inputFile));
} else {
Importer.processFile(reader, tablenameFromFilename(inputFile));
}
}
log.info("Import Complete");
}
}
Based on the limited information about the size of file or performance needs, something like this can be done. This may not be 100% optimized, but just an example. You may also have to do some exception handling in the main method, since the new method might throw an IOException:
import org.apache.commons.io.FileUtils; // Add this import statement at the top
// Moved this statement outside the for loop, as it seems there is no need to fetch the archive directory path multiple times.
Path archiveDir = Paths.get(applicationContext.getEnvironment().getProperty("betl..archive.dir"));
for(Path inputFile : pathsToProcess) {
// Added this code
if(checkIfFileMatches(inputFile, archiveDir); {
// Add the logger here.
}
//Added the else condition, so that if the files do not match, only then you read, process in DB and move the file over to the archive.
else {
// read in the file:
readFile(inputFile.toAbsolutePath().toString());
Files.move(inputFile, archiveDir.resolve(inputFile.getFileName()),StandardCopyOption.REPLACE_EXISTING);
}
}
//Added this method to check if the source file and the target file contents are same.
// This will need an import of the FileUtils class. You may change the approach to use any other utility file, or read the data byte by byte and compare. If the files are very large, probably better to use Buffered file reader.
private boolean checkIfFileMatches(Path sourceFilePath, Path targetDirectoryPath) throws IOException {
if (sourceFilePath != null) { // may not need this check
File sourceFile = sourceFilePath.toFile();
String fileName = sourceFile.getName();
File targetFile = new File(targetDirectoryPath + "/" + fileName);
if (targetFile.exists()) {
return FileUtils.contentEquals(sourceFile, targetFile);
}
}
return false;
}

Create and delete a zip file

I wrote a JUnit 5 test and I need to create a zip file, put some text files on it and remove the zip file.
I don't have any problem for creating the zip file and the text files inside it, but whenever I call file.delete() it returns false.
I even tried to create an empty zip file and it also fails to delete it. Is there a way to solve this ?
static File file;
#BeforeAll
static void setUp() throws IOException {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file=File.createTempFile("tempDir",".zip")));
ZipEntry e = new ZipEntry("emptyFile.txt");
out.putNextEntry(e);
out.closeEntry();
e = new ZipEntry("oneLineFile.txt");
out.putNextEntry(e);
StringBuilder sb;
byte[] data;
sb = new StringBuilder();
sb.append("route_id,agency_id,route_short_name,route_long_name,route_type");
data = sb.toString().getBytes();
out.write(data, 0, data.length);
out.closeEntry();
out.close();
}
#AfterAll
static void set(){
file.delete(); // return false
}
If you don't create too many temporary files use File.deleteOnExit():
Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates. Files (or directories) are deleted in the reverse order that they are registered. Invoking this method to delete a file or directory that is already registered for deletion has no effect. Deletion will be attempted only for normal termination of the virtual machine, as defined by the Java Language Specification.
File system operations can be unsafe e.g. Windows likes to lock the files used by the JVM process.
Moreover your test code can terminate with exception without properly closing ZipOutputStream potentially locking the file and preventing deletion. You should use finally:
ZipOutputStream out = null;
try {
out = ...
} finally {
if (out != null)
out.close();
}

reading an external file using TextIO

I don't understand how to use TextIO's readFile(String Filename)
Can someone please explain how can I read an external file?
public static void readFile(String fileName) {
if (fileName == null) // Go back to reading standard input
readStandardInput();
else {
BufferedReader newin;
try {
newin = new BufferedReader( new FileReader(fileName) );
}
catch (Exception e) {
throw new IllegalArgumentException("Can't open file \"" + fileName + "\" for input.\n"
+ "(Error :" + e + ")");
}
if (! readingStandardInput) { // close current input stream
try {
in.close();
}
catch (Exception e) {
}
}
emptyBuffer(); // Added November 2007
in = newin;
readingStandardInput = false;
inputErrorCount = 0;
inputFileName = fileName;
}
}
I had to use TextIO for a school assignment and I got stuck on it too. The problem I had was that using the Scanner class I could just pass the name of the file as long as the file was in the same folder as my class.
Scanner fileScanner = new Scanner("data.txt");
That works fine. But with TextIO, this won't work;
TextIO.readfile("data.txt"); // can't find file
You have to include the path to the file like this;
TextIo.readfile("src/package/data.txt");
Not sure if there is a way to get it to work like the Scanner class or not, but this is what I've been doing in my course at school.
The above answer (about using the correct file name) is correct, however, as a clarification, make sure that you actually use the proper file path. The file path suggested above, i.e. src/package/ will not work in all circumstances. While this will be obvious to some, for those of you who need clarification, keep reading.
For example (and I use NetBeans), if you have already moved the file into NetBeans, and the file is already in the folder you want it to be in, then right click on the folder itself, and click 'properties'. Then expand the 'file path' section by clicking on the three dots next to the hidden file path. You will see the actual file path in its entirety.
For example, if the entire file path is:
C:\Users..\NetBeansProjects\IceCream\src\icecream\icecream.dat
Then, in the java code file itself, you can write:
TextIo.readfile("src/icecream/icecream.dat");
In other words, make sure you include the words 'src' but also everything that follows the src as well. If it's in the same folder as the rest of the files, you won't need anything prior to the 'src'.

What am i doing wrong with copying my file?

So I am trying to copy one file from one place to the other using the solution found here :
Copying files from one directory to another in Java
My code creates the new directory but cant seem to find the file ,even though the landedtitlesFile is pointing to the proper path and file. I always get my "blast" comment in case you were wondering if my program gets to the end of the method.
Thank you for your time and patience.
private File landedtitlesFile = new File("C:\\Program Files (x86)\\Steam\\SteamApps\\common\\Crusader Kings II\\common\\landed_titles\\landed_titles.txt");
private String modPath = "C:\\Users\\Bernard\\Documents\\Paradox Interactive\\Crusader Kings II\\mod\\viking";
public void createCopyLandedTitles(Boolean vanilla){
if (vanilla == true) {
File dir = new File(modPath + "\\common\\landed_titles");
dir.mkdir();
try{
FileUtils.copyFile(landedtitlesFile,dir);
}
catch (IOException e ){
System.out.println("blast");
}
}
copyFile expects the second parameter to be the destination file, not a destination directory. You need to give it the target name of the file within that directory:
FileUtils.copyFile(
landedtitlesFile,
new File(dir, landedtitlesFile.getName());
Exception objects generally contain some information on the cause. If you print out the exception with e.printStackTrace(); (or rethrow it up the stack with throw new RuntimeException(e);) then you will be able to see what it says.

Get file case-sensitive name, with case-insensitive spelling

I am making an application where the user picks a file from:
FilePicker.PickFile(filename)
where filename is a string.
In the method, it will translate into:
File file = new File(filename);
and nothing is wrong with that. Next, I do,
if(file.exists()){
System.out.println(file.getName());
}
else{
System.out.println("Fail.");
}
and this is where the problem begins. I want to get the name of the file, say "HELLO.txt," but if filename is "hello.txt," it still passes the file.exists() check, and file.getName() returns as "hello.txt," not "HELLO.txt". Is there a way, to return file.getName() as the case-sensitive version as "HELLO.txt?" Thanks!
An example:
HELLO.txt is the real file
FilePicker.PickFile("hello.txt");
OUTPUT:
hello.txt
When you are using Windows, which is case preserving (FAT32/NTFS/..), you can use file.getCanonicalFile().getName() to get the canonical name of the selected file.
When you are using Linux or Android and you want to select a file based on a file name that does not necessarily match case, iterate through all files in the file's directory (file.getParent()), and pick the one that .equalsIgnoreCase the filename. Or see Case-insensitive File.equals on case-sensitive file system
/**
* Maps lower case strings to their case insensitive File
*/
private static final Map<String, File> insensitiveFileHandlerCache = new HashMap<String, File>();
/**
* Case insensitive file handler. Cannot return <code>null</code>
*/
public static File newFile(String path) {
if (path == null)
return new File(path);
path = path.toLowerCase();
// First see if it is cached
if (insensitiveFileHandlerCache.containsKey(path)) {
return insensitiveFileHandlerCache.get(path);
} else {
// If it is not cached, cache it (the path is lower case)
File file = new File(path);
insensitiveFileHandlerCache.put(path, file);
// If the file does not exist, look for the real path
if (!file.exists()) {
// get the directory
String parentPath = file.getParent();
if (parentPath == null) {
// No parent directory? -> Just return the file since we can't find the real path
return file;
}
// Find the real path of the parent directory recursively
File dir = Util.newFile(parentPath);
File[] files = dir.listFiles();
if (files == null) {
// If it is not a directory
insensitiveFileHandlerCache.put(path, file);
return file;
}
// Loop through the directory and put everything you find into the cache
for (File otherFile : files) {
// the path of our file will be updated at this point
insensitiveFileHandlerCache.put(otherFile.getPath().toLowerCase(), otherFile);
}
// if you found what was needed, return it
if (insensitiveFileHandlerCache.containsKey(path)) {
return insensitiveFileHandlerCache.get(path);
}
}
// Did not find it? Return the file with the original path
return file;
}
}
Use
File file = newFile(path);
instead of
File file = new File(path);
It's backed by a cache so it shouldn't be too slow. Did a few test runs and it seems to work. It recursively checks the the parent directories to see if they do have the correct letter cases. Then it lists for each directory all files and caches their correct letter casing. In the end it checks if the file with the path has been found and returns the file with the case sensitive path.
Looks like in Java 7 and above on Windows, you can use Path#toRealPath(NOFOLLOW_LINKS) and it would be more correct than getCanonicalFile() in the presence of symlinks.

Categories

Resources