我最近开发了一个应用程序并创建了jar文件。
我的一个类创建了一个输出目录,用它的资源填充文件。
我的代码是这样的:
// Copy files from dir "template" in this class resource to output.
private void createOutput(File output) throws IOException {
File template = new File(FileHelper.URL2Path(getClass().getResource("template")));
FileHelper.copyDirectory(template, output);
}
不幸的是,这不起作用。
我没有运气就尝试了以下内容:
使用Streams来解决类似的问题 在其他课程,但它不起作用 与dirs。代码类似于 http://www.exampledepot.com/egs/java.io/CopyFile.html
使用new File(getClass().getResource("template").toUri())
在写这篇文章的时候,我正在思考而不是在资源路径中有一个模板目录,而是有一个zip文件。这样做我可以将文件作为inputStream并将其解压缩到我需要的位置。但我不确定这是否正确。
答案 0 :(得分:16)
感谢您的解决方案!对于其他人,以下内容不使用辅助类(StringUtils除外)
/ 我为此解决方案添加了额外信息,请检查代码的结尾,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();
}
}
它仅使用Apache Software Foundation中的一个外部库,但所使用的功能仅为:
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;
}
我的知识仅限于Apache许可证,但您可以在没有库的代码中使用此方法。但是,如果存在许可证问题,我不负责。
答案 1 :(得分:11)
我认为使用zip文件的方法很有意义。大概你会做一个getResourceAsStream
来获取zip的内部,这在逻辑上看起来就像一个目录树。
骨架方法:
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
}
答案 2 :(得分:11)
使用Java7 +可以通过创建FileSystem
然后使用walkFileTree
递归复制文件来实现。
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;
}
});
}
该方法可以这样使用:
copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar"))
答案 3 :(得分:6)
我讨厌使用之前发布的ZIP文件方法,所以我想出了以下内容。
public void copyResourcesRecursively(URL originUrl, File destination) throws Exception {
URLConnection urlConnection = originUrl.openConnection();
if (urlConnection instanceof JarURLConnection) {
copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection);
} else if (urlConnection instanceof FileURLConnection) {
FileUtils.copyFilesRecursively(new File(originUrl.getPath()), destination);
} else {
throw new Exception("URLConnection[" + urlConnection.getClass().getSimpleName() +
"] is not a recognized/implemented connection type.");
}
}
public void copyJarResourcesRecursively(File destination, JarURLConnection jarConnection ) throws IOException {
JarFile jarFile = jarConnection.getJarFile();
for (JarEntry entry : CollectionUtils.iterable(jarFile.entries())) {
if (entry.getName().startsWith(jarConnection.getEntryName())) {
String fileName = StringUtils.removeStart(entry.getName(), jarConnection.getEntryName());
if (!entry.isDirectory()) {
InputStream entryInputStream = null;
try {
entryInputStream = jarFile.getInputStream(entry);
FileUtils.copyStream(entryInputStream, new File(destination, fileName));
} finally {
FileUtils.safeClose(entryInputStream);
}
} else {
FileUtils.ensureDirectoryExists(new File(destination, fileName));
}
}
}
}
示例用法(将类路径资源“config”中的所有文件复制到“$ {homeDirectory} / config”:
File configHome = new File(homeDirectory, "config/");
//noinspection ResultOfMethodCallIgnored
configHome.mkdirs();
copyResourcesRecursively(super.getClass().getResource("/config"), configHome);
这既适用于复制平面文件,也适用于Jar文件。
注意:上面的代码使用了一些自定义实用程序类(FileUtils,CollectionUtils)以及一些来自Apache commons-lang(StringUtils)的代码,但这些函数的命名应该非常明显。
答案 4 :(得分:4)
lpiepiora的答案,是对的!但是有一个小问题, 来源,应该是一个jar url。当源路径是文件系统的路径时,上面的代码将无法正常工作。 要解决此问题,您应该使用ReferencePath,代码,您可以从以下链接获取: Read from file system via FileSystem object copyFromJar的新代码应该是:
public class ResourcesUtils {
public static void copyFromJar(final String sourcePath, final Path target) throws URISyntaxException,
IOException {
final PathReference pathReference = PathReference.getPath(new URI(sourcePath));
final Path jarPath = pathReference.getPath();
Files.walkFileTree(jarPath, new SimpleFileVisitor<Path>() {
private Path currentTarget;
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
currentTarget = target.resolve(jarPath.relativize(dir)
.toString());
Files.createDirectories(currentTarget);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
Files.copy(file, target.resolve(jarPath.relativize(file)
.toString()), StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}
public static void main(final String[] args) throws MalformedURLException, URISyntaxException, IOException {
final String sourcePath = "jar:file:/c:/temp/example.jar!/src/main/resources";
ResourcesUtils.copyFromJar(sourcePath, Paths.get("c:/temp/resources"));
}
答案 5 :(得分:3)
我不确定FileHelper
是什么或者做什么,但是你无法直接从JAR复制文件(或目录)。正如您所提到的那样使用InputStream是正确的方法(来自jar或zip):
InputStream is = getClass().getResourceAsStream("file_in_jar");
OutputStream os = new FileOutputStream("dest_file");
byte[] buffer = new byte[4096];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.close();
is.close();
您需要为每个文件执行上述操作(当然适当地处理异常)。您可能会或可能不会(根据您的部署配置)读取有问题的jar文件JarFile(例如,如果部署为非扩展Web应用程序的一部分,则可能无法将其作为实际文件提供) 。如果您可以阅读它,您应该能够遍历JarEntry实例列表,从而重新构建您的目录结构;否则你可能需要将它存储在别处(例如在text或xml资源中)
您可能需要查看Commons IO库 - 它提供了许多常用的流/文件功能,包括复制。
答案 6 :(得分:3)
以下是tess4j项目的工作版本:
/**
* This method will copy resources from the jar file of the current thread and extract it to the destination folder.
*
* @param jarConnection
* @param destDir
* @throws IOException
*/
public void copyJarResourceToFolder(JarURLConnection jarConnection, File destDir) {
try {
JarFile jarFile = jarConnection.getJarFile();
/**
* Iterate all entries in the jar file.
*/
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) {
JarEntry jarEntry = e.nextElement();
String jarEntryName = jarEntry.getName();
String jarConnectionEntryName = jarConnection.getEntryName();
/**
* Extract files only if they match the path.
*/
if (jarEntryName.startsWith(jarConnectionEntryName)) {
String filename = jarEntryName.startsWith(jarConnectionEntryName) ? jarEntryName.substring(jarConnectionEntryName.length()) : jarEntryName;
File currentFile = new File(destDir, filename);
if (jarEntry.isDirectory()) {
currentFile.mkdirs();
} else {
InputStream is = jarFile.getInputStream(jarEntry);
OutputStream out = FileUtils.openOutputStream(currentFile);
IOUtils.copy(is, out);
is.close();
out.close();
}
}
}
} catch (IOException e) {
// TODO add logger
e.printStackTrace();
}
}
答案 7 :(得分:2)
您可以使用ClassLoader获取stream to the resource。获得InputStream后,您可以读取并将流的内容写入OutputStream。
在您的情况下,您需要创建多个OutputStream实例,每个实例对应要复制到目标的每个文件。当然,这要求您事先知道文件名。
对于此任务,最好使用getResourceAsStream,而不是getResource或getResources()。
答案 8 :(得分:1)
我最近遇到过类似问题。我试图从java资源中提取文件夹。所以我用Spring PathMatchingResourcePatternResolver 解决了这个问题。
此代码从指定资源获取所有文件和目录:
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ resourceFolder + "/**");
这是将所有文件和目录从资源复制到磁盘路径的类。
public class ResourceExtractor {
public static final Logger logger =
Logger.getLogger(ResourceExtractor.class);
public void extract(String resourceFolder, String destinationFolder){
try {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ resourceFolder + "/**");
URI inJarUri = new DefaultResourceLoader().getResource("classpath:" + resourceFolder).getURI();
for (Resource resource : resources){
String relativePath = resource
.getURI()
.getRawSchemeSpecificPart()
.replace(inJarUri.getRawSchemeSpecificPart(), "");
if (relativePath.isEmpty()){
continue;
}
if (relativePath.endsWith("/") || relativePath.endsWith("\\")) {
File dirFile = new File(destinationFolder + relativePath);
if (!dirFile.exists()) {
dirFile.mkdir();
}
}
else{
copyResourceToFilePath(resource, destinationFolder + relativePath);
}
}
}
catch (IOException e){
logger.debug("Extraction failed!", e );
}
}
private void copyResourceToFilePath(Resource resource, String filePath) throws IOException{
InputStream resourceInputStream = resource.getInputStream();
File file = new File(filePath);
if (!file.exists()) {
FileUtils.copyInputStreamToFile(resourceInputStream, file);
}
}
}
答案 9 :(得分:0)
我知道这个问题现在有点老了,但是在尝试了一些不起作用的答案以及其他仅需要一个方法就需要整个库的答案之后,我决定将一个类放在一起。它不需要第三方库,并且已经通过Java 8进行了测试。共有四种公共方法:copyResourcesToTempDir
,copyResourcesToDir
,copyResourceDirectory
和jar
。
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.util.Enumeration;
import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* A helper to copy resources from a JAR file into a directory.
*/
public final class ResourceCopy {
/**
* URI prefix for JAR files.
*/
private static final String JAR_URI_PREFIX = "jar:file:";
/**
* The default buffer size.
*/
private static final int BUFFER_SIZE = 8 * 1024;
/**
* Copies a set of resources into a temporal directory, optionally preserving
* the paths of the resources.
* @param preserve Whether the files should be placed directly in the
* directory or the source path should be kept
* @param paths The paths to the resources
* @return The temporal directory
* @throws IOException If there is an I/O error
*/
public File copyResourcesToTempDir(final boolean preserve,
final String... paths)
throws IOException {
final File parent = new File(System.getProperty("java.io.tmpdir"));
File directory;
do {
directory = new File(parent, String.valueOf(System.nanoTime()));
} while (!directory.mkdir());
return this.copyResourcesToDir(directory, preserve, paths);
}
/**
* Copies a set of resources into a directory, preserving the paths
* and names of the resources.
* @param directory The target directory
* @param preserve Whether the files should be placed directly in the
* directory or the source path should be kept
* @param paths The paths to the resources
* @return The temporal directory
* @throws IOException If there is an I/O error
*/
public File copyResourcesToDir(final File directory, final boolean preserve,
final String... paths) throws IOException {
for (final String path : paths) {
final File target;
if (preserve) {
target = new File(directory, path);
target.getParentFile().mkdirs();
} else {
target = new File(directory, new File(path).getName());
}
this.writeToFile(
Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(path),
target
);
}
return directory;
}
/**
* Copies a resource directory from inside a JAR file to a target directory.
* @param source The JAR file
* @param path The path to the directory inside the JAR file
* @param target The target directory
* @throws IOException If there is an I/O error
*/
public void copyResourceDirectory(final JarFile source, final String path,
final File target) throws IOException {
final Enumeration<JarEntry> entries = source.entries();
final String newpath = String.format("%s/", path);
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
if (entry.getName().startsWith(newpath) && !entry.isDirectory()) {
final File dest =
new File(target, entry.getName().substring(newpath.length()));
final File parent = dest.getParentFile();
if (parent != null) {
parent.mkdirs();
}
this.writeToFile(source.getInputStream(entry), dest);
}
}
}
/**
* The JAR file containing the given class.
* @param clazz The class
* @return The JAR file or null
* @throws IOException If there is an I/O error
*/
public Optional<JarFile> jar(final Class<?> clazz) throws IOException {
final String path =
String.format("/%s.class", clazz.getName().replace('.', '/'));
final URL url = clazz.getResource(path);
Optional<JarFile> optional = Optional.empty();
if (url != null) {
final String jar = url.toString();
final int bang = jar.indexOf('!');
if (jar.startsWith(ResourceCopy.JAR_URI_PREFIX) && bang != -1) {
optional = Optional.of(
new JarFile(
jar.substring(ResourceCopy.JAR_URI_PREFIX.length(), bang)
)
);
}
}
return optional;
}
/**
* Writes an input stream to a file.
* @param input The input stream
* @param target The target file
* @throws IOException If there is an I/O error
*/
private void writeToFile(final InputStream input, final File target)
throws IOException {
final OutputStream output = Files.newOutputStream(target.toPath());
final byte[] buffer = new byte[ResourceCopy.BUFFER_SIZE];
int length = input.read(buffer);
while (length > 0) {
output.write(buffer, 0, length);
length = input.read(buffer);
}
input.close();
output.close();
}
}
答案 10 :(得分:0)
您可以使用我的图书馆: 编译组:“ com.github.ardenliu”,名称:“ arden-file”,版本:“ 0.0.4”
ResourcesUtils类: copyFromClassPath(最终字符串resourcePath,最终路径targetRoot)
Junit测试: Eclipse类路径的一个测试案例;另一个罐子 https://github.com/ardenliu/common/blob/master/arden-file/src/test/java/com/github/ardenliu/common/file/ResourcesUtilsTest.java