Java is the key here. I need to be able to delete files but users expect to be able to "undelete" from the recycle bin. As far as I can tell this isn't possible. Anyone know otherwise?
Ten years later, with Java 9, finally there is a builtin way to move files to the Trash Bin
java.awt.Desktop.moveToTrash(java.io.File):
public boolean moveToTrash(File file)
Moves the specified file to the trash.
Parameters:
file - the file
Returns:
returns true if successfully moved the file to the trash.
The availability of this feature for the underlying platform can be tested with Desktop.isSupported(Desktop.Action.MOVE_TO_TRASH).
For various reasons Windows has no concept of a folder that simply corresponds to the Recycle Bin.
The correct way is to use JNI to invoke the Windows SHFileOperation API, setting the FO_DELETE flag in the SHFILEOPSTRUCT structure.
SHFileOperation documention
Java example for copying a file using SHFileOperation (the Recycle Bin link in the same article doesn't work)
Java 9 has new method but in my case I am restricted to Java 8.
I found Java Native Access Platform that has hasTrash() and moveToTrash() method. I tested it on Win 10 and Mac OS (Worked) for me.
static boolean moveToTrash(String filePath) {
File file = new File(filePath);
FileUtils fileUtils = FileUtils.getInstance();
if (fileUtils.hasTrash()) {
try {
fileUtils.moveToTrash(new File[] { file });
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
} else {
System.out.println("No Trash");
return false;
}
}
Maven Repository
https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform/5.1.0
Don't confuse It is Java Native Access Platform not Java Native Access
See the fileutil incubator project (part of the Java Desktop Integration Components project):
This incubator project is created to host those file utility functionalities, most of which are extensions to the java.io.File class in J2SE. There are frequent requests from Java developers for such features like: sending a file to trash bin, checking free disk space, accessing file attributes etc. This project addresses such frequently requested APIs.
Note, this should work not only on Windows, but on other platforms (Linux, Mac OS X) as well.
My 3 cents - use cmd util Recycle.exe with -f to force recycle (no prompt). Works perfectly.
public class Trash {
public void moveToTrash(File ... file) throws IOException {
moveToTrash(false, file);
}
public void promptMoveToTrash(File ... file) throws IOException {
moveToTrash(true, file);
}
private void moveToTrash(boolean withPrompt, File ... file) throws IOException {
String fileList = Stream.of(file).map(File::getAbsolutePath).reduce((f1, f2)->f1+" "+f2).orElse("");
Runtime.getRuntime().exec("Recycle.exe "+(withPrompt ? "" : "-f ")+fileList);
}
}
In JNA platform, the FileUtils doesn't use Win32 API. You should prefer W32FileUtils which supports Undo (restore the file from recycle bin).
Edit: as of the current version of JNA Platform (5.7.0), with FileUtils.getInstance(), this statement has become incorrect, and FileUtils will use the Win32 API.
Related
My CodenameOne app is being tested on the iOS simulator (iPad 8th iOS 14).
It writes some files in the private folder by means of this method:
public void writeFile() throws IOException {
try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(Utils.getRootPath()+DATA_FILE);)
{
os.write(JSONText.getBytes("UTF-8"));
os.flush();
os.close();
} catch(IOException err) {
System.out.println("exception trying to write");
}
}
It works on the CN simulator (writes inside the .cn1/ folder)
but on iOS the exception is catched. The Library folder is of paramount importance on iOS.
Below is the method to get the root path
public static String getRootPath()
{
String documentsRoot=FileSystemStorage.getInstance().getRoots()[0];
String os=Display.getInstance().getPlatformName();
if (os.toLowerCase().contains("ios")) {
int pos=documentsRoot.lastIndexOf("Documents");
if (pos==-1) return documentsRoot+"/";
String libraryRoot=documentsRoot.substring(0,pos)+"Library";
String result=libraryRoot+"/";
return result;
}
The CN version of my app has to write those private files in the same location as the swift version, that is Library.
There is string manipulation, and no extra '/' are added, the file path seems legit.
So the string
file:///Users/mac/Library/Developer/CoreSimulator/Devices/alphanumeric-string/data/Containers/Data/Application/another-alphanumeric-string/Documents/
is transformed and
the getRootPath() method returns
file:///Users/mac/Library/Developer/CoreSimulator/Devices/alphanumeric-string/data/Containers/Data/Application/another-alphanumeric-string/Library/
But there is exception.
Furthermore, at some point after the writing attempt, I see in the console output something I think is relevant:
Failed to create directory /Users/mac/Library/Developer/CoreSimulator/Devices/alphanumeric-string/data/Containers/Data/Application/another-alphanumeric-string/Documents/cn1storage/
What is this? Is it related to my problem?
Is CN filesystem access broken or flawed?
I know io access permissions are automatically created by the CN compiler, but are they working?
So how to fix my issue about the Library folder?
The cn1storage printout just means the storage directory already exists.
The way to get the library path is this: Getting an iOS application's "~/Library" path reliably
You need to use that approach. I think your assumption that Document and Library reside under the exact same hierarchy is just incorrect.
I am using zip4j to do decompression, but now I need to decompress the .zip file without original folder structure.
e.g.
desktop/abc.zip/
/a
/b/
/x.txt
/y.txt
/z.txt
I want to extract all files inside the abc.zip directly to desktop.
e.g.
desktop/x.txt
/y.txt
/z.txt
Since the .zip file will be protected by password, I cannot do this by java.util.zip library. I did some research on my requirement but in vain. I also tried to review/rewrite the source code of zip4j, but it seems to be beyond my capability.
Do I miss any setting from zip4j which could help me to achieve it easily or is there any other java library suitable on this requirement?
Below is the source code of part of my program:
public class FileDecompressor {
void decompressFiles(String sourceFile, String fileDestination) {
decompressFiles(sourceFile, fileDestination, "");
}
void decompressFiles(String sourceFile, String fileDestination, String zipPassword) {
try {
ZipFile zipFile = new ZipFile(sourceFile);
if (zipFile.isEncrypted()) {
zipFile.setPassword(zipPassword);
}
zipFile.extractAll(fileDestination);
} catch (ZipException e) {
e.printStackTrace();
}
}
}
It isn't "direct" as such but you could unzip to a temp directory and move the contents to the desktop.
An advantage would be that if you use move on the same drive the entire directory would appear on the desktop at one time instead of it appearing part by part.
I'm trying to load a dll file from a jar to work with the applet with this fix:
Extract and load DLL from JAR
I create the file, set it writable and executable, but when I check it with canWrite()/canExecute()
it returns false. The applet is signed.
The code:
private static void loadLib() {
URL res = SystemActivityNotifications.class.getResource("/sys-native
/sysactivitynotifications.dll");
InputStream in = res.openStream();
File dll = new File(path + "sysactivitynotifications.dll");
dll.setExecutable(true);
dll.setWritable(true);
logger.info(dll.canWrite() + " " + dll.canExecute());
FileOutputStream fos = new FileOutputStream(dll);
byte[] array = new byte[1024];
try {
for(int i = in.read(array); i != 1; i=in.read(array)) {
fos.write(array, 0, i);
}
} catch (IOException e) { logger.info("Cannot write to file: " + e.getMessage()); }
fos.close();
in.close();
System.load(dll.getAbsolutePath());
}
The file is created properly, but it throws an exception while trying to write to it.
edit: it writes to the file the second time I run the applet, but if I delete the file and run again the first iteration doesn't work.
Forgot to mention: all the code above is from a catch block after System.load.library(dll); throws the exception.
try
{
System.loadLibrary("sysactivitynotifications");
ptr = allocAndInit();
if (ptr == -1)
ptr = 0;
}
catch (Throwable t)
{
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
else {
loadLib();
}
}
Edit: it throws me this error:
java.lang.UnsatisfiedLinkError: C:\: The process cannot access the file because it is being used by another process
..trying to load a dll file from a jar to work with the applet..
For deploying Java applets, the best option is usually to launch the applet using Java Web Start. JWS works on Windows, OS X & *nix.
Java Web Start (JWS) is the Oracle Corporation technology used to launch rich client (Swing, Java-FX, AWT, SWT..) desktop applications directly from a network or internet link. It offers 'one click' installation for platforms that support Java.
JWS provides many appealing features including, but not limited to, splash screens, desktop integration, file associations, automatic update (including lazy downloads and programmatic control of updates), partitioning of natives & other resource downloads by platform, architecture or locale, configuration of run-time environment (minimum J2SE version, run-time options, RAM etc.), easy management of common resources using extensions..
Place the natives in the root of a (signed) Jar and put a reference to that Jar in the launch file and they will be placed on the class-path of the app.
How can I change the current working directory from within a Java program? Everything I've been able to find about the issue claims that you simply can't do it, but I can't believe that that's really the case.
I have a piece of code that opens a file using a hard-coded relative file path from the directory it's normally started in, and I just want to be able to use that code from within a different Java program without having to start it from within a particular directory. It seems like you should just be able to call System.setProperty( "user.dir", "/path/to/dir" ), but as far as I can figure out, calling that line just silently fails and does nothing.
I would understand if Java didn't allow you to do this, if it weren't for the fact that it allows you to get the current working directory, and even allows you to open files using relative file paths....
There is no reliable way to do this in pure Java. Setting the user.dir property via System.setProperty() or java -Duser.dir=... does seem to affect subsequent creations of Files, but not e.g. FileOutputStreams.
The File(String parent, String child) constructor can help if you build up your directory path separately from your file path, allowing easier swapping.
An alternative is to set up a script to run Java from a different directory, or use JNI native code as suggested below.
The relevant OpenJDK bug was closed in 2008 as "will not fix".
If you run your legacy program with ProcessBuilder, you will be able to specify its working directory.
There is a way to do this using the system property "user.dir". The key part to understand is that getAbsoluteFile() must be called (as shown below) or else relative paths will be resolved against the default "user.dir" value.
import java.io.*;
public class FileUtils
{
public static boolean setCurrentDirectory(String directory_name)
{
boolean result = false; // Boolean indicating whether directory was set
File directory; // Desired current working directory
directory = new File(directory_name).getAbsoluteFile();
if (directory.exists() || directory.mkdirs())
{
result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null);
}
return result;
}
public static PrintWriter openOutputFile(String file_name)
{
PrintWriter output = null; // File to open for writing
try
{
output = new PrintWriter(new File(file_name).getAbsoluteFile());
}
catch (Exception exception) {}
return output;
}
public static void main(String[] args) throws Exception
{
FileUtils.openOutputFile("DefaultDirectoryFile.txt");
FileUtils.setCurrentDirectory("NewCurrentDirectory");
FileUtils.openOutputFile("CurrentDirectoryFile.txt");
}
}
It is possible to change the PWD, using JNA/JNI to make calls to libc. The JRuby guys have a handy java library for making POSIX calls called jnr-posix. Here's the maven info
As mentioned you can't change the CWD of the JVM but if you were to launch another process using Runtime.exec() you can use the overloaded method that lets you specify the working directory. This is not really for running your Java program in another directory but for many cases when one needs to launch another program like a Perl script for example, you can specify the working directory of that script while leaving the working dir of the JVM unchanged.
See Runtime.exec javadocs
Specifically,
public Process exec(String[] cmdarray,String[] envp, File dir) throws IOException
where dir is the working directory to run the subprocess in
If I understand correctly, a Java program starts with a copy of the current environment variables. Any changes via System.setProperty(String, String) are modifying the copy, not the original environment variables. Not that this provides a thorough reason as to why Sun chose this behavior, but perhaps it sheds a little light...
The working directory is a operating system feature (set when the process starts).
Why don't you just pass your own System property (-Dsomeprop=/my/path) and use that in your code as the parent of your File:
File f = new File ( System.getProperty("someprop"), myFilename)
The smarter/easier thing to do here is to just change your code so that instead of opening the file assuming that it exists in the current working directory (I assume you are doing something like new File("blah.txt"), just build the path to the file yourself.
Let the user pass in the base directory, read it from a config file, fall back to user.dir if the other properties can't be found, etc. But it's a whole lot easier to improve the logic in your program than it is to change how environment variables work.
I have tried to invoke
String oldDir = System.setProperty("user.dir", currdir.getAbsolutePath());
It seems to work. But
File myFile = new File("localpath.ext");
InputStream openit = new FileInputStream(myFile);
throws a FileNotFoundException though
myFile.getAbsolutePath()
shows the correct path.
I have read this. I think the problem is:
Java knows the current directory with the new setting.
But the file handling is done by the operation system. It does not know the new set current directory, unfortunately.
The solution may be:
File myFile = new File(System.getPropety("user.dir"), "localpath.ext");
It creates a file Object as absolute one with the current directory which is known by the JVM. But that code should be existing in a used class, it needs changing of reused codes.
~~~~JcHartmut
You can use
new File("relative/path").getAbsoluteFile()
after
System.setProperty("user.dir", "/some/directory")
System.setProperty("user.dir", "C:/OtherProject");
File file = new File("data/data.csv").getAbsoluteFile();
System.out.println(file.getPath());
Will print
C:\OtherProject\data\data.csv
You can change the process's actual working directory using JNI or JNA.
With JNI, you can use native functions to set the directory. The POSIX method is chdir(). On Windows, you can use SetCurrentDirectory().
With JNA, you can wrap the native functions in Java binders.
For Windows:
private static interface MyKernel32 extends Library {
public MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class);
/** BOOL SetCurrentDirectory( LPCTSTR lpPathName ); */
int SetCurrentDirectoryW(char[] pathName);
}
For POSIX systems:
private interface MyCLibrary extends Library {
MyCLibrary INSTANCE = (MyCLibrary) Native.loadLibrary("c", MyCLibrary.class);
/** int chdir(const char *path); */
int chdir( String path );
}
The other possible answer to this question may depend on the reason you are opening the file. Is this a property file or a file that has some configuration related to your application?
If this is the case you may consider trying to load the file through the classpath loader, this way you can load any file Java has access to.
If you run your commands in a shell you can write something like "java -cp" and add any directories you want separated by ":" if java doesnt find something in one directory it will go try and find them in the other directories, that is what I do.
Use FileSystemView
private FileSystemView fileSystemView;
fileSystemView = FileSystemView.getFileSystemView();
currentDirectory = new File(".");
//listing currentDirectory
File[] filesAndDirs = fileSystemView.getFiles(currentDirectory, false);
fileList = new ArrayList<File>();
dirList = new ArrayList<File>();
for (File file : filesAndDirs) {
if (file.isDirectory())
dirList.add(file);
else
fileList.add(file);
}
Collections.sort(dirList);
if (!fileSystemView.isFileSystemRoot(currentDirectory))
dirList.add(0, new File(".."));
Collections.sort(fileList);
//change
currentDirectory = fileSystemView.getParentDirectory(currentDirectory);
I have developed a number of classes which manipulate files in Java. I am working on a Linux box, and have been blissfully typing new File("path/to/some/file");. When it came time to commit I realised some of the other developers on the project are using Windows. I would now like to call a method which can take in a String of the form "/path/to/some/file" and, depending on the OS, return a correctly separated path.
For example:
"path/to/some/file" becomes "path\\to\\some\\file" on Windows.
On Linux it just returns the given String.
I realise it wouldn't take long to knock up a regular expression that could do this, but I'm not looking to reinvent the wheel, and would prefer a properly tested solution. It would be nice if it was built in to the JDK, but if it's part of some small F/OSS library that's fine too.
So is there a Java utility which will convert a String path to use the correct File separator char?
Apache Commons comes to the rescue (again). The Commons IO method FilenameUtils.separatorsToSystem(String path) will do what you want.
Needless to say, Apache Commons IO will do a lot more besides and is worth looking at.
A "/path/to/some/file" actually works under Windows Vista and XP.
new java.io.File("/path/to/some/file").getAbsoluteFile()
> C:\path\to\some\file
But it is still not portable as Windows has multiple roots. So the root directory has to be selected in some way. There should be no problem with relative paths.
Edit:
Apache commons io does not help with envs other than unix & windows. Apache io source code:
public static String separatorsToSystem(String path) {
if (path == null) {
return null;
}
if (isSystemWindows()) {
return separatorsToWindows(path);
} else {
return separatorsToUnix(path);
}
}
This is what Apache commons-io does, unrolled into a couple of lines of code:
String separatorsToSystem(String res) {
if (res==null) return null;
if (File.separatorChar=='\\') {
// From Windows to Linux/Mac
return res.replace('/', File.separatorChar);
} else {
// From Linux/Mac to Windows
return res.replace('\\', File.separatorChar);
}
}
So if you want to avoid the extra dependency, just use that.
With the new Java 7 they have included a class called Paths this allows you to do exactly what you want (see http://docs.oracle.com/javase/tutorial/essential/io/pathOps.html)
here is an example:
String rootStorePath = Paths.get("c:/projects/mystuff/").toString();
Do you have the option of using
System.getProperty("file.separator")
to build the string that represents the path?
For anyone trying to do this 7 years later, the apache commons separatorsToSystem method has been moved to the FilenameUtils class:
FilenameUtils.separatorsToSystem(String path)
I create this function to check if a String contain a \ character then convert them to /
public static String toUrlPath(String path) {
return path.indexOf('\\') < 0 ? path : path.replace('\\', '/');
}
public static String toUrlPath(Path path) {
return toUrlPath(path.toString());
}
String fileName = Paths.get(fileName).toString();
Works perfectly with Windows at least even with mixed paths, for example
c:\users\username/myproject\myfiles/myfolder
becomes
c:\users\username\myproject\myfiles\myfolder
Sorry haven't check what Linux would make of the above but there again Linux file structure is different so you wouldn't search for such a directory
I think there is this hole in Java Paths.
String rootStorePath = Paths.get("c:/projects/mystuff/").toString();
works if you are running it on a system that has the file system you need to use. As pointed out, it used the current OS file system.
I need to work with paths between windows and linux, say to copy a file from one to another. While using "/" every works I guess if you are using all Java commands, but I need to make an sftp call so using / or file.separator etc... does not help me. I cannot use Path() because it converts mine to the default file system I am running on "now".
What Java needs is:
on windows system:
Path posixPath = Paths.get("/home/mystuff", FileSystem.Posix );
stays /home/mystuff/ and does not get converted to \\home\\mystuff
on linux system:
String winPath = Paths.get("c:\home\mystuff", FileSystem.Windows).toString();
stays c:\home\mystuff and does not get converted to /c:/home/mystuff
similar to working with character sets:
URLEncoder.encode( "whatever here", "UTF-8" ).getBytes();
P.S. I also do not want to load a whole apache io jar file to do something simple either. In this case they do not have what I propose anyways.
Shouldn't it be enough to say:
"path"+File.Seperator+"to"+File.Seperator+"some"+File.Seperator+"file"