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();
}
Related
This is my code, which I want to create method, that accept file and move it in my pc of specified folder. I just make this copy existing file of text to another text file, but I want to move in specified folder, not copy. How to solve this problem?
public static void main(String[] args) {
InputStream inStream = null;
OutputStream outStream = null;
try {
File afile = new File("C:\\Users\\anar.memmedov\\Desktop\\test.txt");
File bfile = new File("C:\\Users\\anar.memmedov\\Desktop\\ok\\test3.txt");
inStream = new FileInputStream(afile);
outStream = new FileOutputStream(bfile);
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
//delete the original file
afile.delete();
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}
}
You can simply use Files.move: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#move(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)
Move or rename a file to a target file.
By default, this method attempts to move the file to the target file, failing if the target file exists except if the source and target are the same file, in which case this method has no effect. If the file is a symbolic link then the symbolic link itself, not the target of the link, is moved. This method may be invoked to move an empty directory. In some implementations a directory has entries for special files or links that are created when the directory is created. In such implementations a directory is considered empty when only the special entries exist. When invoked to move a directory that is not empty then the directory is moved if it does not require moving the entries in the directory. For example, renaming a directory on the same FileStore will usually not require moving the entries in the directory. When moving a directory requires that its entries be moved then this method fails (by throwing an IOException). To move a file tree may involve copying rather than moving directories and this can be done using the copy method in conjunction with the Files.walkFileTree utility method.
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFolder\\" + sourcePath.getFileName());
Files.move(sourcePath, targetPath);
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;
}
I am currently making a game which have levels which are pre-made and i currently store them in resources. I want a solution to how i can extract a folder out of a jar in production and development environment.
I have tried copying the folder by using the given method below and pass the src as File defaultWorld = new File(GameData.class.getClassLoader().getResource("worlds/").getFile());
and destination as private static File worldsDir = new File("run/worlds");
public static void copyFolder(File src, File dest) {
try {
if (src.isDirectory()) {
if (!dest.exists()) {
dest.mkdir();
}
String[] files = src.list();
for (String file : files) {
copyFolder(new File(src, file), new File(dest, file));
}
} else {
try (InputStream in = new FileInputStream(src)) {
try (OutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
I expected the above method to work on both dev and production env but it throws FileNotFoundException when opening file output stream.
You cannot list resources in a jar.
Any workarounds you’re thinking of, are unreliable.
Never call the getFile() method of URL. It does not return a valid file name; it just returns the path and query portions of the URL, with any percent-escapes intact. Furthermore, jar entries are not file: URLs, so a resource path can never be a valid file name when it refers to a jar entry.
The only way to list things in a jar file is by iterating through all jar entries, but you aren’t even guaranteed to have access to your jar, because ClassLoaders are not guaranteed to be URLClassLoaders, and in general are not guaranteed to use jar: URLs.
You can’t even rely on MyApplication.class.getProtectionDomain().getCodeSource(), because getCodeSource() can return null.
If you want to copy multiple files from your jar, here are some reliable ways to do it:
Hard code the list of resources you plan to copy. It’s your application, so you know what files you’re putting in the jar.
Keep a single text file in your jar which contains a list of resource paths to copy.
Store your resources in a single zip archive which is embedded in your jar, and extract it yourself with a ZipInputStream which wraps MyApplication.class.getResourceAsStream.
In a project I'm working on in Windows 7, using JDK 7u25, I have a class that stores the state for the application. That class has a static save method to save the state to disk. I originally had the following code. (state is a JSONObject and I'm assuming that the output directory hasn't necessarily been created when this function is called.)
public State {
private static String stateFile = "\\state\\state.json";
public static void save() {
try {
File sFile = new File(stateFile);
sFile.mkdirs();
sFile.setWritable(true);
FileWriter file = new FileWriter(sFile);
String str = state.toString(4);
file.write(str);
file.close();
} catch (IOException ex) {
HLogger.log(ex.getMessage());
}
}
}
Whenever I ran this code, it logged an IOException - complaining that access to the state file was denied. I discovered that if I changed the save function (as shown below) it would work exactly as expected.
public static void save() {
try {
File sFile = new File(stateFile);
File path = new File(sFile.getParent());
path.mkdirs();
sFile.setWritable(true);
FileWriter file = new FileWriter(sFile);
String str = state.toString(4);
file.write(str);
file.close();
} catch (IOException ex) {
HLogger.log(ex.getMessage());
}
}
Can anyone explain why it's necessary to create the output directory in a separate file object in order to obtain write permission for the file?
This is not permissions failure but is a misuse of FileWriter.
In the first code snippet a directory named state.json is being created by the call to mkdirs() because it:
Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories. Note that if this operation fails it may have succeeded in creating some of the necessary parent directories.
and then an attempt to write to the directory using a FileWriter is made, which fails. The following is the throws clause From FileWriter(File):
IOException - if the file exists but is a directory rather than a regular file, does not exist but cannot be created, or cannot be opened for any other reason
The second snippet creates a directory named state (because it is using the parent of \\state\\state.json) and then a file named state.json, and therefore uses FileWriter on a regular file which succeeds.
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
}
}