I managed to find two example snippets of code for zipping a directory with Java:
public static void pack(final Path folder, final Path zipFilePath) throws IOException {
try (
FileOutputStream fos = new FileOutputStream(zipFilePath.toFile());
ZipOutputStream zos = new ZipOutputStream(fos)
) {
Files.walkFileTree(folder, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
zos.putNextEntry(new ZipEntry(folder.relativize(file).toString()));
Files.copy(file, zos);
zos.closeEntry();
return FileVisitResult.CONTINUE;
}
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
zos.putNextEntry(new ZipEntry(folder.relativize(dir).toString() + "/"));
zos.closeEntry();
return FileVisitResult.CONTINUE;
}
});
}
}
and
public static void pack(String sourceDirPath, String zipFilePath) throws IOException {
Path p = Files.createFile(Paths.get(zipFilePath));
try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p))) {
Path pp = Paths.get(sourceDirPath);
Files.walk(pp)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
try {
zs.putNextEntry(zipEntry);
zs.write(Files.readAllBytes(path));
zs.closeEntry();
} catch (Exception e) {
System.err.println(e);
}
});
}
}
But, for both examples, I can't for the life of me figure out how to exclude certain sub-directories within the source directories from being included in the output zip.
Could somebody lend me a hand?
Thank you so much!
The short answer is to define your directory filter in the preVisitDirectory(...) method such that it returns FileVisitResult.SKIP_SUBTREE whenever it pre-visits a directory that you would like to exclude.
For more details, see the Controlling the Flow section of Walking the File Tree.
Edit:
As requested, a sample implementation using the above provided code. Create an instance of it with paths to the source directory (srcPath) and Zip file name (zipPath). Add any directory names to be excluded. For example, addDirExclude( "bin" ) would exclude any directory named bin, its files, and any sub-directories thereunder.
This is an example, intended to demonstration one of several ways to further control a file tree walk. This is not production quality code; use at your own risk.
public class ZipWithExcludedDirs {
final private Path srcPath;
final private Path zipPath;
final private List<String> excludeList = new ArrayList<>();
public ZipWithExcludedDirs( Path srcPath, Path zipPath ) {
this.srcPath = srcPath;
this.zipPath = zipPath;
}
public void addDirExclude( String exDir ) {
excludeList.add( exDir );
}
public void pack() throws IOException {
try ( FileOutputStream fos = new FileOutputStream( zipPath.toFile() );
ZipOutputStream zos = new ZipOutputStream( fos ) ) {
Files.walkFileTree( srcPath, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
throws IOException {
zos.putNextEntry( new ZipEntry( file.toString() ) );
Files.copy( file, zos );
zos.closeEntry();
return FileVisitResult.CONTINUE;
}
public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs )
throws IOException {
String dirName = dir.getFileName().toString();
for ( String excl : excludeList )
if ( dirName.equals( excl ) )
return FileVisitResult.SKIP_SUBTREE;
zos.putNextEntry( new ZipEntry( dir.toString() + "/" ) );
zos.closeEntry();
return FileVisitResult.CONTINUE;
}
} );
}
}
}
Edit (Part Deux)
I have edited the above code such that it returns SKIP_SUBTREE instead of SKIP_SIBLINGS, which is what I had originally, but changed for some reason. A glance at the JavaDocs appears to indicate that SKIP_SUBTREE and SKIP_SIBLINGS have the same effect on the directory being visited. It does. However SKIP_SIBLINGS also affects siblings of the directory (i.e. files and directories that follow it in the same parent directory).
Furthermore, the original file walker code referenced by OP, causes an erroneous artifact to be included. This was due to the "relativizing" of the ZipEntry path. Paths should not adjusted in SimpleFileVistor. If there is a need for an archive to be relative or
absolute, then the original srcPath should set as such.
Related
I need to get all the absolute file path of the files with extension .pdf. I am using the code mentioned below, but I'm able to only get the absolute file path of only one file.
How can I modify the code to get all the absolute file paths ?
public class FindFiles {
String absoluteFilePath = "";
String fileName;
public String PdfFiles(String parentDirectory, String fileExtension) {
FileFilter fileFilter = new FileFilter(fileExtension);
File parentDir = new File(parentDirectory);
// Put the names of all files ending with .pdf in a String array
String[] listOfTextFiles = parentDir.list(fileFilter);
if (listOfTextFiles.length == 0) {
System.out.println("There are no files in this direcotry!");
}
for (String file : listOfTextFiles) {
//construct the absolute file paths...
absoluteFilePath = new StringBuffer(parentDirectory).append(File.separator).append(file).toString();
fileName = file.toString();
}
return absoluteFilePath;
}
public static void main(String args[]) {
FindFiles f = new FindFiles();
f.PdfFiles("", "");
}
}
you are overrding absoluteFilePath every time in the loop;
try with
absoluteFilePath += new StringBuffer(parentDirectory).append(File.separator).append(file).toString();
Don't be bothered, use java.nio.file:
final Path dir = Paths.get(baseDir).toAbsolutePath();
final String filter = "*." + extension;
final List<Path> ret = new ArrayList<>();
try (
final DirectoryStream<Path> dirstream
= Files.newDirectoryStream(dir, filter);
) {
for (final Path entry: dirstream)
ret.add(entry);
}
return ret;
If you use Java 8, it's even more simple.
A more efficient solution for your problem can be given using classes in java.nio package.
E.g. For checking whether a file is a pdf file or not, use Files.probeContentType(Path path).
Instead of writing loops to visit all directories and files inside these directories, use Files.walkFileTree(Path start, FileVisitor<? super Path> visitor)
Solution for your problem using these classes are
public class FindPdfFiles {
public static void main(String[] args) throws IOException{
final Path path = Paths.get("C:\\SearchDirectoryForPDF");
Files.walkFileTree(path, new FindPdfFilesFilter());
}
}
class FindPdfFilesFilter extends SimpleFileVisitor<Path> {
#Override
public FileVisitResult visitFile(Path path, BasicFileAttributes arg1)
throws IOException {
final String mimeTypeOfFile = Files.probeContentType(path);
if(mimeTypeOfFile != null && !mimeTypeOfFile.isEmpty() && mimeTypeOfFile.toLowerCase().contains("pdf")) {
System.out.println(path.toString());
}
return FileVisitResult.CONTINUE;
}
}
I have a folder with 3 picture inside of them which I wish to zip and email. I have a method that does this which I've used with previous problems and it works fine. However this time it keeps generating an invalid zip and when I open the zip it only has 1 picture inside with a size of 0.
I can't seems to figure out why though. This is the method:
//generate the zip file for the picture
String zipFile = context.getExternalFilesDir(null) + "/ArcFlash/Checklist.zip";
String srcDir = context.getExternalFilesDir(null) + "/ArcFlash/CheckListMedia";
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
File srcFile = new File(srcDir);
addDirToArchive(zos, srcFile, context);
here is my addDirToArchive method which generates the zip:
private static void addDirToArchive(ZipOutputStream zos, File srcFile, Context ctx)
{
File[] files = srcFile.listFiles();
for (int i = 0; i < files.length; i++)
{
// if the file is directory, use recursion
if (files[i].isDirectory())
{
addDirToArchive(zos, files[i], ctx);
continue;
}
try
{
System.out.println("tAdding file: " + files[i].getName());
// create byte buffer
byte[] buffer = new byte[1024];//2048
FileInputStream fis = new FileInputStream(files[i]);
String target = ctx.getExternalFilesDir(null) + "/";
String oldPath = files[i].getPath();
String newPath = oldPath.replace(target, "");
zos.putNextEntry(new ZipEntry(newPath));
int length;
while ((length = fis.read(buffer)) > 0)
{
zos.write(buffer, 0, length);
}
zos.closeEntry();
// close the InputStream
fis.close();
}
catch (Exception ex)
{
Log.i("customException", "error zipping: " + ex.getMessage());
}
}
}
EDIT
Using the code samples below, here is how to do what you want:
final Path basePath = Paths.get(context.getExternalFilesDir(null));
final Path srcDir = Paths.resolve("ArcFlash/CheckListMedia");
final Path zipFile = Paths.resolve("ArcFlash/Checklist.zip");
final Map<String, Object> env = new HashMap<>();
env.put("create", "true");
final URI zip = URI.create("jar:file:" + zipFile.toAbsolutePath().toString());
try (
final FileSystem fs = FileSystems.newFileSystem(zip, env, null);
) {
Files.walkFileTree(srcDir, new CopyFileVisitor(srcDir, fs.getPath("/")));
}
First, a sample of how to create a zip file:
public final class ZipZip
{
public static void main(final String... args)
throws IOException
{
final Map<String, Object> env = new HashMap<>();
env.put("create", "true");
final URI zip = URI.create("jar:file:/tmp/t.zip");
final Path sourceFile = Paths.get("/tmp/foo.txt");
Files.deleteIfExists(Paths.get("/tmp/t.zip"));
try (
final FileSystem fs = FileSystems.newFileSystem(zip, env, null);
) {
final Path zipdir = fs.getPath("/dir");
Files.createDirectory(zipdir);
final Path zipfile = zipdir.resolve("t.txt");
Files.copy(sourceFile, zipfile);
}
}
}
Then, I have recently written a FileVisitor to recursively copy a directory, which is used here; here is its code:
public final class CopyFileVisitor
implements FileVisitor<Path>
{
private final Path srcdir;
private final Path dstdir;
public CopyFileVisitor(final Path srcdir, final Path dstdir)
{
this.srcdir = srcdir.toAbsolutePath();
this.dstdir = dstdir.toAbsolutePath();
}
#Override
public FileVisitResult preVisitDirectory(final Path dir,
final BasicFileAttributes attrs)
throws IOException
{
Files.createDirectories(toDestination(dir));
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs)
throws IOException
{
System.out.printf("%s -> %s\n", file.toAbsolutePath(),
toDestination(file));
Files.copy(file, toDestination(file));
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException
{
throw exc;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException
{
if (exc != null)
throw exc;
return FileVisitResult.CONTINUE;
}
private Path toDestination(final Path victim)
{
final Path tmp = victim.toAbsolutePath();
final Path rel = srcdir.relativize(tmp);
return dstdir.resolve(rel.toString());
}
}
I strongly recommend you to use this library for zipping/unzipping contents:
http://www.lingala.net/zip4j/
Be sure you are adding correct headers while making the file.
i am a nibble in java. i have my own efforts to get the things done. but certainly i am facing a challenge. i have a dummy program that searches for files of a particular extension(.txt) supplied as a command line argument. i am trying to make file objects of these searched file for further manipulations. but i can't understand how to do this in my code.. here is my code sample...
public class Find {
public static class Finder extends SimpleFileVisitor<Path> {
private final PathMatcher matcher;
private int numMatches = 0;
Finder(String pattern) {
matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
}
void find(Path file) {
Path name = file.getFileName();
if (name != null && matcher.matches(name)) {
numMatches++;
System.out.println(file);
}
}
// Prints the total number of
// matches to standard out.
void done() {
System.out.println("Matched: "+ numMatches);
}
// Invoke the pattern matching
// method on each file.
//#Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) {
find(file);
return CONTINUE;
}
// Invoke the pattern matching
// method on each directory.
//#Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) {
find(dir);
return CONTINUE;
}
//#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println(exc);
return CONTINUE;
}
}
public static void main(String[] args) throws IOException {
Iterable<Path> root;
root = FileSystems.getDefault().getRootDirectories();
// System.out.println(name.getAbsolutePath());
for (Path startingDir : FileSystems.getDefault().getRootDirectories()) {
String pattern = args[0];
Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, finder);
}
}
}
here is what i am trying to do. the output of my program is a long list of text files with their absolute path. now i want to make objects of these files so that i can upload these to a URL. to upload them i have to make a stream with file object to be sent..how to get absoluteFilename?? to get this you must have a file object...right.... and my revised question is : how to make file objects of searched files???
FileInputStream fileInputStream = null;
try {
new FileInputStream("absoluteFilename");
byte[] buffer = new byte[MAX_SIZE];
int bufferIndex = 0;
while (fileInputStream.available() > 0) {
buffer[bufferIndex++] = (byte) fileInputStream.read();
}
byte[] fileContent = new byte[bufferIndex];
System.arraycopy(buffer,0,fileContent,0,bufferIndex);
URL serverUrl = new URL(url);
URLConnection connection = serverURL.openConnection();
connection.setConnectTimeout(60000);
connection.getOutputStream().write(fileContent);
} catch (Exception fatal) {
//proper handling??
I need upload folder with subfolders on amazon s3. I try upload with this snipet.
for (Path path : directoryWalk("/home/rmuhamedgaliev/tmp/eota7tas0cdlg2ufq5mlke7olf/")){
if (!path.getParent().toString().equals("eota7tas0cdlg2ufq5mlke7olf")){
amazonS3Client.putObject("*****", "/plans/eota7tas0cdlg2ufq5mlke7olf/" + path.getParent().toString() + "/" + path.getFileName(), new File(path.toString()));
} else {
amazonS3Client.putObject("*******", "/plans/eota7tas0cdlg2ufq5mlke7olf/" + path.getFileName(), new File(path.toString()));
}
}
But this code create full path files with ("/home/rmuhamedgaliev/tmp/eota7tas0cdlg2ufq5mlke7olf"). How to upload it with path ("/plans/eota7tas0cdlg2ufq5mlke7olf/{subfolders and files}")
private List<Path> directoryWalk(String path) throws IOException {
final List<Path> files = new ArrayList<>();
Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
files.add(file);
return FileVisitResult.CONTINUE;
}
});
return files;
}
Have you looked at the TransferManager in the AWS SDK for Java? You could use the uploadDirectory method for this. The javadoc is here. In essence, you could do something like this:
transferManager.uploadDirectory(bucketName, "plans/eota7tas0cdlg2ufq5mlke7olf/", new File("/home/rmuhamedgaliev/tmp/eota7tas0cdlg2ufq5mlke7olf/"), true);
I writen my own way.
List<File> files = new LinkedList<File>();
listFiles(new File("/home/rmuhamedgaliev/tmp/eota7tas0cdlg2ufq5mlke7olf"), files, true);
for (File f : files) {
String key = f.getAbsolutePath().substring(new File("/home/rmuhamedgaliev/tmp/eota7tas0cdlg2ufq5mlke7olf").getAbsolutePath().length() + 1)
.replaceAll("\\\\", "/");
amazonS3Client.putObject("****", "plans/eota7tas0cdlg2ufq5mlke7olf/" + key, f);
}
This question already has answers here:
Copying files from one directory to another in Java
(34 answers)
Closed 7 years ago.
Method to copy entire directory contents to another directory in java or groovy?
FileUtils.copyDirectory()
Copies a whole directory
to a new location preserving the file
dates. This method copies the
specified directory and all its child
directories and files to the specified
destination. The destination is the
new location and name of the
directory.
The destination directory is created
if it does not exist. If the
destination directory did exist, then
this method merges the source with the
destination, with the source taking
precedence.
To do so, here's the example code
String source = "C:/your/source";
File srcDir = new File(source);
String destination = "C:/your/destination";
File destDir = new File(destination);
try {
FileUtils.copyDirectory(srcDir, destDir);
} catch (IOException e) {
e.printStackTrace();
}
The following is an example of using JDK7.
public class CopyFileVisitor extends SimpleFileVisitor<Path> {
private final Path targetPath;
private Path sourcePath = null;
public CopyFileVisitor(Path targetPath) {
this.targetPath = targetPath;
}
#Override
public FileVisitResult preVisitDirectory(final Path dir,
final BasicFileAttributes attrs) throws IOException {
if (sourcePath == null) {
sourcePath = dir;
} else {
Files.createDirectories(targetPath.resolve(sourcePath
.relativize(dir)));
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs) throws IOException {
Files.copy(file,
targetPath.resolve(sourcePath.relativize(file)));
return FileVisitResult.CONTINUE;
}
}
To use the visitor do the following
Files.walkFileTree(sourcePath, new CopyFileVisitor(targetPath));
If you'd rather just inline everything (not too efficient if you use it often, but good for quickies)
final Path targetPath = // target
final Path sourcePath = // source
Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(final Path dir,
final BasicFileAttributes attrs) throws IOException {
Files.createDirectories(targetPath.resolve(sourcePath
.relativize(dir)));
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs) throws IOException {
Files.copy(file,
targetPath.resolve(sourcePath.relativize(file)));
return FileVisitResult.CONTINUE;
}
});
With Groovy, you can leverage Ant to do:
new AntBuilder().copy( todir:'/path/to/destination/folder' ) {
fileset( dir:'/path/to/src/folder' )
}
AntBuilder is part of the distribution and the automatic imports list which means it is directly available for any groovy code.
public static void copyFolder(File source, File destination)
{
if (source.isDirectory())
{
if (!destination.exists())
{
destination.mkdirs();
}
String files[] = source.list();
for (String file : files)
{
File srcFile = new File(source, file);
File destFile = new File(destination, file);
copyFolder(srcFile, destFile);
}
}
else
{
InputStream in = null;
OutputStream out = null;
try
{
in = new FileInputStream(source);
out = new FileOutputStream(destination);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0)
{
out.write(buffer, 0, length);
}
}
catch (Exception e)
{
try
{
in.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
try
{
out.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
This is my piece of Groovy code for that. Tested.
private static void copyLargeDir(File dirFrom, File dirTo){
// creation the target dir
if (!dirTo.exists()){
dirTo.mkdir();
}
// copying the daughter files
dirFrom.eachFile(FILES){File source ->
File target = new File(dirTo,source.getName());
target.bytes = source.bytes;
}
// copying the daughter dirs - recursion
dirFrom.eachFile(DIRECTORIES){File source ->
File target = new File(dirTo,source.getName());
copyLargeDir(source, target)
}
}
Use Apache's
FileUtils.copyDirectory
Write
your own e.g. this guy provides
example code.
Java 7: take a look at java.nio.file.Files.
With coming in of Java NIO, below is a possible solution too
With Java 9:
private static void copyDir(String src, String dest, boolean overwrite) {
try {
Files.walk(Paths.get(src)).forEach(a -> {
Path b = Paths.get(dest, a.toString().substring(src.length()));
try {
if (!a.toString().equals(src))
Files.copy(a, b, overwrite ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{});
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (IOException e) {
//permission issue
e.printStackTrace();
}
}
With Java 7:
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
Path sourceParentFolder = Paths.get("/sourceParent");
Path destinationParentFolder = Paths.get("/destination/");
try {
Stream<Path> allFilesPathStream = Files.walk(sourceParentFolder);
Consumer<? super Path> action = new Consumer<Path>(){
#Override
public void accept(Path t) {
try {
String destinationPath = t.toString().replaceAll(sourceParentFolder.toString(), destinationParentFolder.toString());
Files.copy(t, Paths.get(destinationPath));
}
catch(FileAlreadyExistsException e){
//TODO do acc to business needs
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
allFilesPathStream.forEach(action );
} catch(FileAlreadyExistsException e) {
//file already exists and unable to copy
} catch (IOException e) {
//permission issue
e.printStackTrace();
}
}
}
Neither FileUtils.copyDirectory() nor Archimedes's answer copy directory attributes (file owner, permissions, modification times, etc).
https://stackoverflow.com/a/18691793/14731 provides a complete JDK7 solution that does precisely that.
With regard to Java, there is no such method in the standard API. In Java 7, the java.nio.file.Files class will provide a copy convenience method.
References
The Java Tutorials
Copying files from one directory to another in Java
If you're open to using a 3rd party library, check out javaxt-core. The javaxt.io.Directory class can be used to copy directories like this:
javaxt.io.Directory input = new javaxt.io.Directory("/source");
javaxt.io.Directory output = new javaxt.io.Directory("/destination");
input.copyTo(output, true); //true to overwrite any existing files
You can also provide a file filter to specify which files you want to copy. There are more examples here:
http://javaxt.com/javaxt-core/io/Directory/Directory_Copy