Copy directory from a jar file
I think your approach of using a zip file makes sense. Presumably you'll do a getResourceAsStream
to get at the internals of the zip, which will logically look like a directory tree.
A skeleton approach:
InputStream is = getClass().getResourceAsStream("my_embedded_file.zip");
ZipInputStream zis = new ZipInputStream(is);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// do something with the entry - for example, extract the data
}
Thanks for the solution! For others, the following doesn't make use of the auxiliary classes (except for StringUtils)
/I added extra information for this solution, check the end of the code, Zegor V/
public class FileUtils {
public static boolean copyFile(final File toCopy, final File destFile) {
try {
return FileUtils.copyStream(new FileInputStream(toCopy),
new FileOutputStream(destFile));
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyFilesRecusively(final File toCopy,
final File destDir) {
assert destDir.isDirectory();
if (!toCopy.isDirectory()) {
return FileUtils.copyFile(toCopy, new File(destDir, toCopy.getName()));
} else {
final File newDestDir = new File(destDir, toCopy.getName());
if (!newDestDir.exists() && !newDestDir.mkdir()) {
return false;
}
for (final File child : toCopy.listFiles()) {
if (!FileUtils.copyFilesRecusively(child, newDestDir)) {
return false;
}
}
}
return true;
}
public static boolean copyJarResourcesRecursively(final File destDir,
final JarURLConnection jarConnection) throws IOException {
final JarFile jarFile = jarConnection.getJarFile();
for (final Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) {
final JarEntry entry = e.nextElement();
if (entry.getName().startsWith(jarConnection.getEntryName())) {
final String filename = StringUtils.removeStart(entry.getName(), //
jarConnection.getEntryName());
final File f = new File(destDir, filename);
if (!entry.isDirectory()) {
final InputStream entryInputStream = jarFile.getInputStream(entry);
if(!FileUtils.copyStream(entryInputStream, f)){
return false;
}
entryInputStream.close();
} else {
if (!FileUtils.ensureDirectoryExists(f)) {
throw new IOException("Could not create directory: "
+ f.getAbsolutePath());
}
}
}
}
return true;
}
public static boolean copyResourcesRecursively( //
final URL originUrl, final File destination) {
try {
final URLConnection urlConnection = originUrl.openConnection();
if (urlConnection instanceof JarURLConnection) {
return FileUtils.copyJarResourcesRecursively(destination,
(JarURLConnection) urlConnection);
} else {
return FileUtils.copyFilesRecusively(new File(originUrl.getPath()),
destination);
}
} catch (final IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyStream(final InputStream is, final File f) {
try {
return FileUtils.copyStream(is, new FileOutputStream(f));
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyStream(final InputStream is, final OutputStream os) {
try {
final byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
is.close();
os.close();
return true;
} catch (final IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean ensureDirectoryExists(final File f) {
return f.exists() || f.mkdir();
}
}
It uses only one external library from the Apache Software Foundation, however the used functions are only :
public static String removeStart(String str, String remove) {
if (isEmpty(str) || isEmpty(remove)) {
return str;
}
if (str.startsWith(remove)){
return str.substring(remove.length());
}
return str;
}
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
My knowledge is limited on Apache licence, but you can use this methods in your code without library. However, i am not responsible for licence issues, if there is.
Using Java7+ this can be achieved by creating FileSystem
and then using walkFileTree
to copy files recursively.
public void copyFromJar(String source, final Path target) throws URISyntaxException, IOException {
URI resource = getClass().getResource("").toURI();
FileSystem fileSystem = FileSystems.newFileSystem(
resource,
Collections.<String, String>emptyMap()
);
final Path jarPath = fileSystem.getPath(source);
Files.walkFileTree(jarPath, new SimpleFileVisitor<Path>() {
private Path currentTarget;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
currentTarget = target.resolve(jarPath.relativize(dir).toString());
Files.createDirectories(currentTarget);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, target.resolve(jarPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}
The method can be used like this:
copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar"))