How to copy file inside jar to outside the jar? - java

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

Related

when using a zip file as a FileSystem the zip file is not being update

I want to manipulate a jar using the standard nio Files and Paths methods. So, Java has a way to do this by creating a zip FileSystem:
try {
zipFS = FileSystems.newFileSystem(zipDisk, zipFSproperties);
} catch(Exception e) {
System.out.println(e.getMessage());
}
My test program uses an existing jar file as a FileSystem and it lists the entries contained in the jar. All that works great. I then copy a new file into the jar and list the entries again. And just as you would expect, the list now contains the newly added file. The problem is after the program closes, I open up the jar file that the jar filesystem is based upon and it doesn't have the new entry added to it. So that's my question! Shouldn't the jar file itself be changed when I add a new entry. I don't know of any commands I can issue the would cause the zip FileSystem to update to the actual jar file that the zip FileSystem wraps. Am I reading more into a FileSystem; are changes in the zip filesystem suppose to cause the corresponding backend zip file to be updated.
code:
public class Main {
public static void main(String[] args) throws IOException {
ZipFileSystem zipFS = new ZipFileSystem("C:\\Temp\\mylibrary\\build\\outputs\\jar\\temp\\mylibrary-debug.zip");
Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().iterator().next().getRoot(),10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
});
paths.forEach( path ->
System.out.println ("zip contains entry: " + path)
);
File file = new File("C:\\Temp\\mylibrary\\src\\main\\java\\com\\phinneyridge\\android\\myLib.java");
System.out.println("copying " + file.getPath());
Path outPath = zipFS.zipFS.getPath("myLib.java");
Files.copy (file.toPath(), outPath, StandardCopyOption.REPLACE_EXISTING);
paths = Files.find(zipFS.zipFS.getPath(""),10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
});
paths.forEach( path ->
System.out.println ("zip contains entry: " + path)
);
}
}
I added code that shows me accessing a zip file, listing the current entries it contains, adding a new entry (via file copy), and lastly listing the contents again. All of this code works correctly. What doesn't work is that the changes to the zip filesystem don't get incorporated back into the zip file when the application ends. I was surprised that the zip file didn't get updated, but I'm now under the opinion, that it's working as it is intended to work; not doing what I wanted it to do, but that's okay. I can't find any documentation that says it would update the jar file that the FileSystem object originated from. So I'm basically asking is that the correct behavior, or is there something I'm entirely missing to cause the zip FileSystem object to update the Zip file?
Here's the code when I tried Dunc suggestion:
public static void main(String[] args) throws IOException {
ZipFileSystem zipFS = new ZipFileSystem("C:\\Temp\\mylibrary\\build\\outputs\\jar\\temp\\mylibrary-debug.zip");
try (FileSystem fs = zipFS.zipFS) {
try (Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().
iterator().next().getRoot(), 10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
})) {
paths.forEach(path ->
System.out.println("zip contains entry: " + path)
);
}
File file = new File("C:\\Temp\\mylibrary\\src\\main\\java\\com\\phinneyridge\\android\\myLib.java");
System.out.println("copying " + file.getPath());
Path outPath = fs.getPath("myLib.java");
Files.copy(file.toPath(), outPath, StandardCopyOption.REPLACE_EXISTING);
try (Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().
iterator().next().getRoot(), 10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
})) {
paths.forEach(path ->
System.out.println("zip contains entry: " + path)
);
}
} catch (Exception e) {
System.out.println("FileSystem Error: " + e.getClass().getName() + " - " + e.getMessage());
e.printStackTrace();
}
}
}
And by the way ZipFileSystem is a wrapper class around the FileSystem. I'll post that code too, incase that's where I 'm doing something wrong.
public class ZipFileSystem {
FileSystem zipFS;
Path zipFSPath;
/**
* Constructor for a ZipFile object
* #param zipFilePath string representing the path to the zipfile. If the path doesn't exist,
* the zip file will be automatically created. If the path exist, it must be a file (not
* a directory) and it must be a valid zip file
*/
public ZipFileSystem(String zipFilePath) {
Map<String, String> zipFSproperties = new HashMap<>();
/* set create to true if you want to create a new ZIP file */
zipFSproperties.put("create", "true");
/* specify encoding to UTF-8 */
zipFSproperties.put("encoding", "UTF-8");
/* Locate File on disk for creation */
URI zipFileUri = new File(zipFilePath).toURI();
URI zipDisk = URI.create("jar:" + zipFileUri);
zipFSPath = Paths.get(zipFileUri);
if (!Files.exists(zipFSPath)) {
try {
createEmptyZipFile(zipFSPath);
} catch (Exception e ) {
System.out.println(e.getMessage());
}
} else {
if (Files.isDirectory(zipFSPath)) {
} else {
try {
// let's open it, which will verify if it's a valid zip file
ZipFile zipFile = new ZipFile(zipFilePath);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
try {
zipFS = FileSystems.newFileSystem(zipDisk, zipFSproperties);
} catch(Exception e) {
System.out.println(e.getMessage());
}
try {
listFiles(zipFS.getPath("/"));
} catch (IOException e) {
e.printStackTrace();
}
}
The correct way to open a zip from a Path - and create if not exists - is:
Path zip = Path.of("/Somepath/to/xyz.zip");
Map<String, String> env = Map.of(
"create", "true"
// other args here ...
);
try (FileSystem fs = FileSystems.newFileSystem(zip, env)) {
// code to read/update here
}
You have not closed any files or streams properly so your changes are probably not flushed back to the file system and will keep hold of file handles which block some operations.
Use try with resources for every operation which will manage the modifications to zip filesystem as well as closing each Stream<Path> from Files.find, and check other places such as createEmptyZipFile for the same problem:
try (FileSystem fs = ... ) {
try (Stream<Path> paths = Files.find(...) ) {
}
Files.copy( ... );
try (Stream<Path> paths = Files.find(...) ) {
}
}
The process cannot access the file because it is being used by another process
You have unnecessary code ZipFile zipFile = new ZipFile(zipFilePath) which tests the zip is valid and you do not call close(), so it will prevent the zip changes being written back. The check can safely be deleted (as FileSystems.newFileSystem does same) or must be wrapped in try() {} so that zipFile is closed before your edits to the zip filesystem.

Create directory in Java but don't throw error if it already exists [duplicate]

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.

Moving from one directory to another java [duplicate]

How do you move a file from one location to another? When I run my program any file created in that location automatically moves to the specified location. How do I know which file is moved?
myFile.renameTo(new File("/the/new/place/newName.file"));
File#renameTo does that (it can not only rename, but also move between directories, at least on the same file system).
Renames the file denoted by this abstract pathname.
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.
If you need a more comprehensive solution (such as wanting to move the file between disks), look at Apache Commons FileUtils#moveFile
With Java 7 or newer you can use Files.move(from, to, CopyOption... options).
E.g.
Files.move(Paths.get("/foo.txt"), Paths.get("bar.txt"), StandardCopyOption.REPLACE_EXISTING);
See the Files documentation for more details
Java 6
public boolean moveFile(String sourcePath, String targetPath) {
File fileToMove = new File(sourcePath);
return fileToMove.renameTo(new File(targetPath));
}
Java 7 (Using NIO)
public boolean moveFile(String sourcePath, String targetPath) {
boolean fileMoved = true;
try {
Files.move(Paths.get(sourcePath), Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
fileMoved = false;
e.printStackTrace();
}
return fileMoved;
}
File.renameTo from Java IO can be used to move a file in Java. Also see this SO question.
To move a file you could also use Jakarta Commons IOs FileUtils.moveFile
On error it throws an IOException, so when no exception is thrown you know that that the file was moved.
Just add the source and destination folder paths.
It will move all the files and folder from source folder to
destination folder.
File destinationFolder = new File("");
File sourceFolder = new File("");
if (!destinationFolder.exists())
{
destinationFolder.mkdirs();
}
// Check weather source exists and it is folder.
if (sourceFolder.exists() && sourceFolder.isDirectory())
{
// Get list of the files and iterate over them
File[] listOfFiles = sourceFolder.listFiles();
if (listOfFiles != null)
{
for (File child : listOfFiles )
{
// Move files to destination folder
child.renameTo(new File(destinationFolder + "\\" + child.getName()));
}
// Add if you want to delete the source folder
sourceFolder.delete();
}
}
else
{
System.out.println(sourceFolder + " Folder does not exists");
}
Files.move(source, target, REPLACE_EXISTING);
You can use the Files object
Read more about Files
You could execute an external tool for that task (like copy in windows environments) but, to keep the code portable, the general approach is to:
read the source file into memory
write the content to a file at the new location
delete the source file
File#renameTo will work as long as source and target location are on the same volume. Personally I'd avoid using it to move files to different folders.
Try this :-
boolean success = file.renameTo(new File(Destdir, file.getName()));
Wrote this method to do this very thing on my own project only with the replace file if existing logic in it.
// we use the older file i/o operations for this rather than the newer jdk7+ Files.move() operation
private boolean moveFileToDirectory(File sourceFile, String targetPath) {
File tDir = new File(targetPath);
if (tDir.exists()) {
String newFilePath = targetPath+File.separator+sourceFile.getName();
File movedFile = new File(newFilePath);
if (movedFile.exists())
movedFile.delete();
return sourceFile.renameTo(new File(newFilePath));
} else {
LOG.warn("unable to move file "+sourceFile.getName()+" to directory "+targetPath+" -> target directory does not exist");
return false;
}
}
Please try this.
private boolean filemovetoanotherfolder(String sourcefolder, String destinationfolder, String filename) {
boolean ismove = false;
InputStream inStream = null;
OutputStream outStream = null;
try {
File afile = new File(sourcefolder + filename);
File bfile = new File(destinationfolder + filename);
inStream = new FileInputStream(afile);
outStream = new FileOutputStream(bfile);
byte[] buffer = new byte[1024 * 4];
int length;
// copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
// delete the original file
afile.delete();
ismove = true;
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}finally{
inStream.close();
outStream.close();
}
return ismove;
}

Working directory when run in eclipse, cmd, and jar

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);
}
}

How do I move a file from one location to another in Java?

How do you move a file from one location to another? When I run my program any file created in that location automatically moves to the specified location. How do I know which file is moved?
myFile.renameTo(new File("/the/new/place/newName.file"));
File#renameTo does that (it can not only rename, but also move between directories, at least on the same file system).
Renames the file denoted by this abstract pathname.
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.
If you need a more comprehensive solution (such as wanting to move the file between disks), look at Apache Commons FileUtils#moveFile
With Java 7 or newer you can use Files.move(from, to, CopyOption... options).
E.g.
Files.move(Paths.get("/foo.txt"), Paths.get("bar.txt"), StandardCopyOption.REPLACE_EXISTING);
See the Files documentation for more details
Java 6
public boolean moveFile(String sourcePath, String targetPath) {
File fileToMove = new File(sourcePath);
return fileToMove.renameTo(new File(targetPath));
}
Java 7 (Using NIO)
public boolean moveFile(String sourcePath, String targetPath) {
boolean fileMoved = true;
try {
Files.move(Paths.get(sourcePath), Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
fileMoved = false;
e.printStackTrace();
}
return fileMoved;
}
File.renameTo from Java IO can be used to move a file in Java. Also see this SO question.
To move a file you could also use Jakarta Commons IOs FileUtils.moveFile
On error it throws an IOException, so when no exception is thrown you know that that the file was moved.
Just add the source and destination folder paths.
It will move all the files and folder from source folder to
destination folder.
File destinationFolder = new File("");
File sourceFolder = new File("");
if (!destinationFolder.exists())
{
destinationFolder.mkdirs();
}
// Check weather source exists and it is folder.
if (sourceFolder.exists() && sourceFolder.isDirectory())
{
// Get list of the files and iterate over them
File[] listOfFiles = sourceFolder.listFiles();
if (listOfFiles != null)
{
for (File child : listOfFiles )
{
// Move files to destination folder
child.renameTo(new File(destinationFolder + "\\" + child.getName()));
}
// Add if you want to delete the source folder
sourceFolder.delete();
}
}
else
{
System.out.println(sourceFolder + " Folder does not exists");
}
Files.move(source, target, REPLACE_EXISTING);
You can use the Files object
Read more about Files
You could execute an external tool for that task (like copy in windows environments) but, to keep the code portable, the general approach is to:
read the source file into memory
write the content to a file at the new location
delete the source file
File#renameTo will work as long as source and target location are on the same volume. Personally I'd avoid using it to move files to different folders.
Try this :-
boolean success = file.renameTo(new File(Destdir, file.getName()));
Wrote this method to do this very thing on my own project only with the replace file if existing logic in it.
// we use the older file i/o operations for this rather than the newer jdk7+ Files.move() operation
private boolean moveFileToDirectory(File sourceFile, String targetPath) {
File tDir = new File(targetPath);
if (tDir.exists()) {
String newFilePath = targetPath+File.separator+sourceFile.getName();
File movedFile = new File(newFilePath);
if (movedFile.exists())
movedFile.delete();
return sourceFile.renameTo(new File(newFilePath));
} else {
LOG.warn("unable to move file "+sourceFile.getName()+" to directory "+targetPath+" -> target directory does not exist");
return false;
}
}
Please try this.
private boolean filemovetoanotherfolder(String sourcefolder, String destinationfolder, String filename) {
boolean ismove = false;
InputStream inStream = null;
OutputStream outStream = null;
try {
File afile = new File(sourcefolder + filename);
File bfile = new File(destinationfolder + filename);
inStream = new FileInputStream(afile);
outStream = new FileOutputStream(bfile);
byte[] buffer = new byte[1024 * 4];
int length;
// copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
// delete the original file
afile.delete();
ismove = true;
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}finally{
inStream.close();
outStream.close();
}
return ismove;
}

Categories