In the springboot project, after the files have been merged, they need to be deleted.
The main code for the merge method is:
// chunkFolder indicates the file storage folder path
Files.list(Paths.get(chunkFolder))
.filter(path -> path.getFileName().toString().contains(HYPHEN))
.sorted((p1, p2) -> {
String fileName1 = p1.getFileName().toString();
String fileName2 = p2.getFileName().toString();
int index1 = fileName1.indexOf(HYPHEN);
int index2 = fileName2.indexOf(HYPHEN);
return Integer.valueOf(fileName1.substring(0, index1)).compareTo(Integer.valueOf(fileName2.substring(0, index2)));
})
.forEach(path -> {
try {
Files.write(Paths.get(target), Files.readAllBytes(path), StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
);
The delete method is:
public void deleteDirectory(Path targetPath) throws IOException {
Files.walk(targetPath).sorted(Comparator.reverseOrder()).forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
});
}
In the windows environment test, delete the storage path after merging. However, the results show that the folder still exists, but cannot be accessed. If you stop the springboot project, the folder disappears.
This problem happens on Windows when you are not closing all the directory streams correctly. You must close all directory streams scanned in your code. The two examples you've shown can be fixed with try with resources:
try(Stream<Path> stream = Files.list( ... )) {
... your code
stream.xyz(...);
}
... plus same for Files.walk() in deleteDirectory. Check other similar calls in all code.
When this occurs the directory is in a strange state when viewed in Windows Explorer - visible but not accessible. Shutting down the VM clears up correctly and the folder disappears from Explorer.
Related
I have a Java application, and when I use java.awt.Desktop:
Desktop.getDesktop().open(file);
It works fine on Windows (opens a file in my default program), but on Ubuntu (with openJdk 13), the Java application gets stuck and I do not even get any log error or anything. I have to force quit the app in order to recover.
The file path it correct, otherwise I would actually get an Exception. Also, isDesktopSupported a isSupported(Action.OPEN) returns true.
What can I do? Can I check some system settings or logs? Or perhaps get some logs from java.awt.Desktop? Or does this not work on Ubuntu/Linux?
Are there any alternatives?
From here:
In order to use the API, you have to call java.awt.EventQueue.invokeLater() and call methods of the Desktop class from a runnable passed to the invokeLater():
void fxEventHandler() {
EQ.invokeLater(() -> {
Desktop.open(...);
});
}
I am just going to add an example function
private static void OpenFile(String filePath){
try
{
//constructor of file class having file as argument
File file = new File(filePath);
if(!Desktop.isDesktopSupported())//check if Desktop is supported by Platform or not
{
System.out.println("not supported");
return;
}
Desktop desktop = Desktop.getDesktop();
if(file.exists()) { //checks file exists or not
EventQueue.invokeLater(() -> {
try {
desktop.open(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
The file downloads properly in eclipse however when i export the jar it always downloads a blank exe. Can anyone help?
public static void downloadAndRunFile(final URL from, final File to) throws Exception {
try (final InputStream in = from.openStream()) {
Files.copy(in, to.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
Desktop.getDesktop().open(to);
}
Actual code being ran
String bub = "https://a.coka.la/bnH6Vg.exe";
try {
Pandora.downloadAndRunFile(
new URL(bub),
File.createTempFile("feelthevluci", ".exe"));
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
The URL in your code seems to return a 404.
I changed it to something that I know works and is safe, and that works both in the IDE and in a jar file.
Check the URL via curl, browser, or other tool to make sure it is working.
I got code like this:
paths.forEach(folderPath -> {
Path to = folderPath.getRoot().resolve(folderPath.getParent().subpath(0, folderPath.getNameCount() - 1)); // До имени (исключительно)
try {
Files.list(folderPath).forEach(filePath -> {
try { Files.move(filePath, to.resolve(filePath.getFileName()), StandardCopyOption.ATOMIC_MOVE); }
catch (IOException e) { processException(e); }
});
if (Files.list(folderPath).count() == 0)
Files.deleteIfExists(folderPath); // this call
} catch (IOException e) { processException(e); }
});
After I call delete methods, I get my empty directory locked (right after it was called, checked it), but not deleted until application is closed. I find it a bit strange, but want to know why is this happening.
(I use Windows 10)
From the documentation of Files.list(Path):
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.
You are not doing this, so the following part of Files.deleteIfExists(…) applies:
On some operating systems it may not be possible to remove a file when it is open and in use by this Java virtual machine or other programs.
You should use
paths.forEach(folderPath -> {
Path to = folderPath.getParent();
try {
try(Stream<Path> files = Files.list(folderPath)) {
files.forEach(filePath -> {
try{Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);}
catch (IOException e) { processException(e); }
});
}
try {
Files.deleteIfExists(folderPath);
} catch(DirectoryNotEmptyException ex) {
// may happen as you continue when Files.move fails,
// but you already reported the original exception then
}
} catch (IOException e) { processException(e); }
});
This closes the stream of files before trying to delete the directory. Note that the second stream operation has been removed, this kind of pre-check is wasteful and should be unneeded when all move operations succeeded. But if some other application inserts a new file concurrently, there is no guaranty that it doesn’t happen between your Files.list(folderPath).count() == 0 check and the subsequent deleteIfExists call.
The cleaner solution would be to remember when a move failed. When no move failed, a still not empty directory should be considered an erroneous situation that should be reported like any other error, e.g.
paths.forEach(folderPath -> {
Path to = folderPath.getParent();
try {
boolean allMovesSucceeded;
try(Stream<Path> files = Files.list(folderPath)) {
allMovesSucceeded = files
.map(filePath -> {
try {
Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);
return true;
}
catch(IOException e) { processException(e); return false; }
}).reduce(Boolean.TRUE, Boolean::logicalAnd);
}
if(allMovesSucceeded) Files.deleteIfExists(folderPath);
} catch (IOException e) { processException(e); }
});
I'm using FileSystem to modify a properties file inside a zip file
public boolean processZip()
{
boolean wasModified = false;
Path zipFilePath = Paths.get(this.getZipFileName());
try (FileSystem fs = FileSystems.newFileSystem(zipFilePath, null))
{
Logger.info("Processing zip: #0",
this.getZipFileName());
Path source = fs.getPath(
"/some/path/some.properties");
// Delete old properties file.
Files.delete(source);
this.createPropertiesFile(source);
wasModified = true;
}
catch (NoSuchFileException e)
{
Logger.info("Properties file not found.");
}
catch (Exception e)
{
Logger.error("There was an error updating the properties in zip: " +
this.getZipFileName(),
e);
}
return wasModified;
}
protected void createPropertiesFile(Path dst) throws IOException
{
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(dst))))
{
bw.write(this.getProperties().getValues());
}
catch (Exception e)
{
Logger.error("There was an error creating the properties in zip: " +
this.getInputJarFileName(),
e);
}
}
But every now and then it fails on windows with the following stacktrace:
ERROR: There was an error updating the properties in zip: C:\Apps\Test\some.zip
java.nio.file.FileSystemException: C:\Apps\Test\some.zip: The process cannot access the file because it is being used by another process.
at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
at java.nio.file.Files.delete(Files.java:1126)
at com.sun.nio.zipfs.ZipFileSystem.sync(ZipFileSystem.java:1294)
at com.sun.nio.zipfs.ZipFileSystem.close(ZipFileSystem.java:277)
The thing is, that file gets copied by an earlier process (the process has ended when the zip file is modified). So I don't know what can be using that file in windows. At first I thought maybe the antivirus but it happens even with the antivirus off.
I'm not sure if there is something else that I can do to prevent this. It is not as frequent but every now and then it happens.
It has admin rights, I have tried exploding the zip, modify the file and zip it back. That only made the issue more frequent. I have tried also Apache commons FileUtils.forceDelete(file).
Is there something I'm missing?
First off, I want to specify that I do have
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
specified in my manifest, and I do check Environment.MEDIA_MOUNTED.
The really strange thing about this, in my opinion, is that it returns true, but it doesn't actually create the directories.
public static void downloadFiles(ArrayList<FileList> list) {
for (FileList file: list) {
try {
// This will be the download directory
File download = new File(downloadDirPatch.getCanonicalPath(), file.getPath());
// downloadDirPatch is defined as follows in a different class:
//
// private static String updateDir = "CognitionUpdate";
// private static File sdcard = Environment.getExternalStorageDirectory();
// final public static File downloadDir = new File(sdcard, updateDir);
// final public static File downloadDirPatch = new File(downloadDir, "patch");
// final public static File downloadDirFile = new File(downloadDir, "file");
if (DEV_MODE)
Log.i(TAG, "Download file: " + download.getCanonicalPath());
// Check if the directory already exists or not
if (!download.exists())
// The directory doesn't exist, so attempt to create it
if (download.mkdirs()) {
// Directory created successfully
Download.download(new URL(file.getUrl() + file.getPatch()), file.getPath(), file.getName(), true);
} else {
throw new ExternalStorageSetupFailedException("Download sub-directories could not be created");
}
else {
// Directory already exists
Download.download(new URL(file.getUrl() + file.getPatch()), file.getPath(), file.getName(), true);
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException ie) {
ie.printStackTrace();
} catch (ExternalStorageSetupFailedException essfe) {
essfe.printStackTrace();
}
}
}
"if (download.mkdirs())" returns true, but when the app goes to actually download the file it throws a
FileNotFoundException: open failed: ENOENT (No such file or directory)
exception, and when I check for the directory afterwards on my phone, it doesn't exist.
Earlier in the program, the app sets up the parent download directory, and that all works fine using File.mkdir(), but File.mkdirs() doesn't seem to be working properly for me.
Your question does not give much detail about the FileNotFoundException. Check the path that triggers this. Forget what you think the path is, log it or run it through the debugger to see what it really is.
As per the directories not created correctly, verify (with your eyes) that the path is really what you think it is. I see you are already logging download.getCanonicalPath, do check in your logs what it really is.
Finally, is Download.download really saving stuff where you think it does? Before you call it you are preparing and verifying a directory using download, but then you are not using download when you call Download.download, so it's impossible to tell.
Btw, don't repeat yourself, you can rewrite without repeating the Download.download line:
if (!download.exists())
if (!download.mkdirs()) {
throw new ExternalStorageSetupFailedException("Download sub-directories could not be created");
}
}
Download.download(new URL(file.getUrl() + file.getPatch()), file.getPath(), file.getName(), true);