I want to read file paths irrespective of whether they are HDFS or local. Currently, I pass the local paths with the prefix file:// and HDFS paths with the prefix hdfs:// and write some code as the following
Configuration configuration = new Configuration();
FileSystem fileSystem = null;
if (filePath.startsWith("hdfs://")) {
fileSystem = FileSystem.get(configuration);
} else if (filePath.startsWith("file://")) {
fileSystem = FileSystem.getLocal(configuration).getRawFileSystem();
}
From here I use the API's of the FileSystem to read the file.
Can you please let me know if there is any other better way than this?
Does this make sense,
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();
conf.addResource(new Path("/hadoop/projects/hadoop-1.0.4/conf/core-site.xml"));
conf.addResource(new Path("/hadoop/projects/hadoop-1.0.4/conf/hdfs-site.xml"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter the file path...");
String filePath = br.readLine();
Path path = new Path(filePath);
FileSystem fs = path.getFileSystem(conf);
FSDataInputStream inputStream = fs.open(path);
System.out.println(inputStream.available());
fs.close();
}
You don't have to put that check if you go this way. Get the FileSystem directly from Path and then do whatever you feel like.
You can get the FileSystem by the following way:
Configuration conf = new Configuration();
Path path = new Path(stringPath);
FileSystem fs = FileSystem.get(path.toUri(), conf);
You do not need to judge if the path starts with hdfs:// or file://. This API will do the work.
Please check the code snippet below that list files from HDFS path; namely the path string that starts with hdfs://. If you can provide Hadoop configuration and local path it will also list files from local file system; namely the path string that starts with file://.
//helper method to get the list of files from the HDFS path
public static List<String> listFilesFromHDFSPath(Configuration hadoopConfiguration, String hdfsPath,
boolean recursive)
{
//resulting list of files
List<String> filePaths = new ArrayList<String>();
FileSystem fs = null;
//try-catch-finally all possible exceptions
try
{
//get path from string and then the filesystem
Path path = new Path(hdfsPath); //throws IllegalArgumentException, all others will only throw IOException
fs = path.getFileSystem(hadoopConfiguration);
//resolve hdfsPath first to check whether the path exists => either a real directory or o real file
//resolvePath() returns fully-qualified variant of the path
path = fs.resolvePath(path);
//if recursive approach is requested
if (recursive)
{
//(heap issues with recursive approach) => using a queue
Queue<Path> fileQueue = new LinkedList<Path>();
//add the obtained path to the queue
fileQueue.add(path);
//while the fileQueue is not empty
while (!fileQueue.isEmpty())
{
//get the file path from queue
Path filePath = fileQueue.remove();
//filePath refers to a file
if (fs.isFile(filePath))
{
filePaths.add(filePath.toString());
}
else //else filePath refers to a directory
{
//list paths in the directory and add to the queue
FileStatus[] fileStatuses = fs.listStatus(filePath);
for (FileStatus fileStatus : fileStatuses)
{
fileQueue.add(fileStatus.getPath());
} // for
} // else
} // while
} // if
else //non-recursive approach => no heap overhead
{
//if the given hdfsPath is actually directory
if (fs.isDirectory(path))
{
FileStatus[] fileStatuses = fs.listStatus(path);
//loop all file statuses
for (FileStatus fileStatus : fileStatuses)
{
//if the given status is a file, then update the resulting list
if (fileStatus.isFile())
filePaths.add(fileStatus.getPath().toString());
} // for
} // if
else //it is a file then
{
//return the one and only file path to the resulting list
filePaths.add(path.toString());
} // else
} // else
} // try
catch(Exception ex) //will catch all exception including IOException and IllegalArgumentException
{
ex.printStackTrace();
//if some problem occurs return an empty array list
return new ArrayList<String>();
} //
finally
{
//close filesystem; not more operations
try
{
if(fs != null)
fs.close();
} catch (IOException e)
{
e.printStackTrace();
} // catch
} // finally
//return the resulting list; list can be empty if given path is an empty directory without files and sub-directories
return filePaths;
} // listFilesFromHDFSPath
If you really want to work with java.io.File API then the following method will help you list files only from local file system; namely path string that starts with file://.
//helper method to list files from the local path in the local file system
public static List<String> listFilesFromLocalPath(String localPathString, boolean recursive)
{
//resulting list of files
List<String> localFilePaths = new ArrayList<String>();
//get the Java file instance from local path string
File localPath = new File(localPathString);
//this case is possible if the given localPathString does not exit => which means neither file nor a directory
if(!localPath.exists())
{
System.err.println("\n" + localPathString + " is neither a file nor a directory; please provide correct local path");
//return with empty list
return new ArrayList<String>();
} // if
//at this point localPath does exist in the file system => either as a directory or a file
//if recursive approach is requested
if (recursive)
{
//recursive approach => using a queue
Queue<File> fileQueue = new LinkedList<File>();
//add the file in obtained path to the queue
fileQueue.add(localPath);
//while the fileQueue is not empty
while (!fileQueue.isEmpty())
{
//get the file from queue
File file = fileQueue.remove();
//file instance refers to a file
if (file.isFile())
{
//update the list with file absolute path
localFilePaths.add(file.getAbsolutePath());
} // if
else //else file instance refers to a directory
{
//list files in the directory and add to the queue
File[] listedFiles = file.listFiles();
for (File listedFile : listedFiles)
{
fileQueue.add(listedFile);
} // for
} // else
} // while
} // if
else //non-recursive approach
{
//if the given localPathString is actually a directory
if (localPath.isDirectory())
{
File[] listedFiles = localPath.listFiles();
//loop all listed files
for (File listedFile : listedFiles)
{
//if the given listedFile is actually a file, then update the resulting list
if (listedFile.isFile())
localFilePaths.add(listedFile.getAbsolutePath());
} // for
} // if
else //it is a file then
{
//return the one and only file absolute path to the resulting list
localFilePaths.add(localPath.getAbsolutePath());
} // else
} // else
//return the resulting list; list can be empty if given path is an empty directory without files and sub-directories
return localFilePaths;
} // listFilesFromLocalPath
This work.
package com.leerhdfs;
//import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import java.io.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
public class ReadWriteHDFSExample {
public static void main(String[] args) throws IOException {
Path inFile = new Path(args[0]);
String destinosrc = args[1];
//InputStream in = new BufferedInputStream(new FileInputStream(localsrc));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(destinosrc), conf);
FSDataInputStream in = fs.open(inFile);
//Progressable ir viendo aumento 10%, 20%, 30%
OutputStream out = fs.create(new Path(destinosrc), new Progressable() {
public void progress() {
System.out.println("Leyendo y escribiendo...");
}
});
IOUtils.copyBytes(in ,out, 4096, true);
in.close();
}
}
Related
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.
In my program I suppose to copy a directory to another directory with java. I did it but the only problem is that for the directories that are inside the source directory get copied but not the file inside them. Why?
This is the assigment:
Ask the user for the source directory and a destination. The source is the directory to be copied; the destination is the directory that will be the parent of the new copy.
First your program should make a new directory in the new location with the same name as the source directory. (You may need to do something special for root directories if you are copying an entire disk. A root directory has no parent directory, and often, no name.)
Then your program should create an array with File class objects for each item in the contents of the source directory, similar to what was done in DirectoryListDemo.
Next , it should iterate the array, and for each item in the array,
if it is a file, copy the file to the new directory using the copyFile() method taken from CopyFileDemoE.
if it is a directory, recursively call this method to copy the directory and all of its contents.
Here is the code:
package com.company;
import java.io.*;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
Scanner src = new Scanner(System.in);
System.out.println("Enter name of source directory to copy from: ");
String sourceFile= src.nextLine();
System.out.println("Enter name of destination directory to copy the files into: ");
String destinationFile = (src.nextLine()+"/"+sourceFile);
isDirFile(sourceFile, destinationFile);
}
public static void isDirFile(String source, String dest) throws Exception{
File sourceFile = new File ("C:/"+source);
File dirFile = new File (dest, new File(source).getName());
dirFile.mkdirs();
File[] entries;
if (dirFile.exists()){
if (sourceFile.isDirectory()){
entries = sourceFile.listFiles();
assert entries != null;
for (File entry:entries){
if(entry.isFile()){
copyFile(entry.getAbsolutePath(),dest);
}
else{
isDirFile(entry.getAbsolutePath(),dest);
}
}
}
}
else{
System.out.println("File does not exist");
}
}
public static void copyFile(String source, String destination) throws Exception{
File sourceFile;
File destFile;
FileInputStream sourceStream;
FileOutputStream destSteam;
BufferedInputStream bufferedSource = null;
BufferedOutputStream bufferedDestination = null;
try{
sourceFile = new File(source);
destFile = new File (destination, new File(source).getName());
sourceStream = new FileInputStream(sourceFile);
destSteam = new FileOutputStream(destFile);
bufferedSource = new BufferedInputStream(sourceStream, 8182);
bufferedDestination = new BufferedOutputStream(destSteam, 8182);
int transfer;
System.out.println("Beginning file copy: ");
System.out.println("\tcopying "+ source);
System.out.println("\tto "+destination);
while ((transfer = bufferedSource.read()) != -1){
bufferedDestination.write(transfer);
}
}
catch (IOException e){
e.printStackTrace();
System.out.println(" An unexpected I/O error occurred.");
}
finally {
if(bufferedSource != null){
bufferedSource.close();
}
if(bufferedDestination != null){
bufferedDestination.close();
}
System.out.println("Files closed. Copy complete.");
}
}
}
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.
I have searched for this answer and I have tried to solve the problem but I can't. I got and Exception of type
No fue posible copiar los archivos. Motivo: C:\Test1 (Acceso denegado)java.io.FileNotFoundException: C:\Test1 (Acceso denegado)
java.io.FileNotFoundException: C:\Test1 (Acceso denegado)
Translated would be something like "It was not possible copying the files. Motive: (Access denied)".
What I am trying to do is to copy a List into a directory recursively.
I could simply copy the files recursively (I already did that) but the requirements are to copy all into a List and then do whatever I want (copy, delete, etc) with the records in the list.
My List contains this records:
C:\Test\Carpeta_A
C:\Test\Carpeta_A\Entrenamiento_1.txt
C:\Test\Carpeta_A\Requerimientos.txt
C:\Test\Carpeta_B
C:\Test\Carpeta_B\queries.txt
C:\Test\Things.txt
Here is my code:
This is the main method.. it calls a method for listing and saving the files and directories and then calls the method for copying the files into another directory preserving my main structure:
public static void main(String[] args) throws IOException
{
String fuente = "C:/Test";
String ruta = "C:/Test1";
teeeeeest listing = new teeeeeest();
List<File> files = listing.getFileListing(fuente);
listing.copyDirectories(files, ruta);
}
public List<File> getFileListing( String fuente ) throws FileNotFoundException
{
List<File> result = getFileListingNoSort(fuente);
Collections.sort(result);
return result;
}
private List<File> getFileListingNoSort( String fuente ) throws FileNotFoundException
{
File source = new File(fuente);
List<File> result = new ArrayList<>();
File[] filesAndDirs = source.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);
for(File file : filesDirs) {
result.add(file); //always add, even if directory
String s = file.getPath().trim();
if (! file.isFile()) {
//must be a directory
//recursive call!
List<File> deeperList = getFileListingNoSort(s);
result.addAll(deeperList);
}
}
return result;
}
public static void copyDirectories(List<File> files, String destiny)
throws IOException
{
InputStream in = null;
OutputStream out = null;
File targetPath = new File(destiny);
System.out.println(targetPath.getPath());
for(int i = 0; i < files.size(); i++)
{
File temp = new File(files.get(i).toString());
//System.out.println(temp.getPath());
try
{
if(temp.isDirectory())
{
if(!targetPath.exists())
{
targetPath.mkdir();
}
File[] filesAndDirs = temp.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);
for(File file : filesDirs)
{
if (! file.isFile())
{
//must be a directory
//recursive call!
copyDirectories(filesDirs,destiny);
}
}
}
else
{
in = new FileInputStream(files.get(i).toString());
out = new FileOutputStream(targetPath);
System.out.println(temp.getPath());
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
}
}
}
catch(Exception e)
{
System.err.println("No fue posible copiar los archivos. Motivo: " + e.getMessage() + e);
e.printStackTrace();
}
}
}
It would be very bad of me to just paste the working code here, it will not help you to think about what the problems are. I will try to give you enough without giving everything: Please mark my answer as accepted if it helps you.
1: You should not sort the file listing. The order the files are read in is important so that you don't get files before directories in your list. That said, it doesn't matter if you sort them because the shorter names, which are the directories, will appear first anyway. Still, don't do work you shouldn't be doing. Remove the getFileListing method and use only the getFileListingNoSort.
List<File> files = listing.getFileListingNoSort(fuente);
2: You need to pass both the source and the destination directories to copyDirectories so that you can make a destination filename from the source filename.
listing.copyDirectories(files, fuente, ruta);
3: You need to create a destination file out of the source filename. There may be better ways, but using simple String parsing will do the trick:
File temp = files.get(i);
String destFileName = destiny + temp.toString().substring(source.length());
File destFile = new File(destFileName);
4: You must create the new directories based on the new destFile. You are using the targetPath, which is only the base directory, not the new directory that needs to be created.
if(!destFile.exists())
{
destFile.mkdir();
}
5: After you make the destination directory, there is nothing else to do. Remove all that code after that up to the 'else'
6: Your outfile should be the new destFile you created.
out = new FileOutputStream(destFile);
7: close your input and output streams or the file copies will not be complete.
in.close();
out.close();
That should get you going. Use an IDE if you can so that you can step through the program with a debugger and see what's happening.
In your copyDirectories method calls the destiny is always the same value, even in recursive calls. You are copying all the files to the same destination file.
So, this question has been asked a million times i believed and I've been reading them for a couple of hours and trying several options given by some people but none of them work for me.
I want to list all the files inside a directory inside the application's JAR, so in IDE this works:
File f = new File(this.getClass().getResource("/resources/").getPath());
for(String s : f.list){
System.out.println(s);
}
That gives me all the files inside the directory.
Now, i've tried this also:
InputStream in = this.getClass().getClassLoader().getResourceAsStream("resources/");
InputStreamReader inReader = new InputStreamReader(in);
Scanner scan = new Scanner(inReader);
while (scan.hasNext()) {
String s = scan.next();
System.out.println("read: " + s);
}
System.out.println("END OF LINE");
And from IDE it prints ALL the files in the directory. Outside IDE prints: "END OF LINE".
Now, I can find an entry inside a Jar with this too:
String s = new File(this.getClass().getResource("").getPath()).getParent().replaceAll("(!|file:\\\\)", "");
JarFile jar = new JarFile(s);
JarEntry entry = jar.getJarEntry("resources");
if (entry != null){
System.out.println("EXISTS");
System.out.println(entry.getSize());
}
That's some horrible coding i had to do to that String.
Anyway... I can't get the list of resources inside the "resources" directory within the Jar... How can I do this???
There's no way to simply get a filtered list of internal resources without first enumerating over the contents of the Jar file.
Luckily, that's actually not that hard (and luckily for me you've done most of the hardwork).
Basically, once you have a reference to the JarFile, you simple need to ask for its' entries and iterate over that list.
By checking the JarEntry name for the required match (ie resources), you can filter the elements you want...
For example...
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ReadMyResources {
public static void main(String[] args) {
new ReadMyResources();
}
public ReadMyResources() {
JarFile jf = null;
try {
String s = new File(this.getClass().getResource("").getPath()).getParent().replaceAll("(!|file:\\\\)", "");
jf = new JarFile(s);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry je = entries.nextElement();
if (je.getName().startsWith("resources")) {
System.out.println(je.getName());
}
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
jf.close();
} catch (Exception e) {
}
}
}
}
Caveat
This type of question actually gets ask a bit. Rather then trying to read the contents of the Jar at runtime, it would be better to produce some kind of text file which contained a list of the available resources.
This could be produced by your build process dynamically before the Jar file is created. It would be a much simpler solution to then read this file in (via getClass().getResource(), for example) and then look up each resource list in the text file...IMHO
For Spring Framework users, have a look at PathMatchingResourcePatternResolver to do something like the following:
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:path/to/resource/*.*");
for (Resource resource : resources) {
InputStream inStream = resource.getInputStream();
// Do something with the input stream
}
My case was to read a directory inside resources:
As my requirement was to transform resource directory to io.File, finally it looked like this:
public static File getResourceDirectory(String resource) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL res = classLoader.getResource(resource);
File fileDirectory;
if ("jar".equals(res.getProtocol())) {
InputStream input = classLoader.getResourceAsStream(resource);
fileDirectory = Files.createTempDir();
List<String> fileNames = IOUtils.readLines(input, StandardCharsets.UTF_8);
fileNames.forEach(name -> {
String fileResourceName = resource + File.separator + name;
File tempFile = new File(fileDirectory.getPath() + File.pathSeparator + name);
InputStream fileInput = classLoader.getResourceAsStream(resourceFileName);
FileUtils.copyInputStreamToFile(fileInput, tempFile);
});
fileDirectory.deleteOnExit();
} else {
fileDirectory = new File(res.getFile());
}
return fileDirectory;
}
If resources are in jar, we copy it to temp directory that will be deleted on application end.
Then calling getResourceDirectory("migrations") returned me io.File directory for further use.