39 Stimmen

Verzeichnis aus einer jar-Datei kopieren

Ich habe kürzlich eine Anwendung entwickelt und die Jar-Datei erstellt.

Eine meiner Klassen erstellt ein Ausgabeverzeichnis und füllt es mit Dateien aus ihrer Ressource.

Mein Code sieht in etwa so aus:

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

Leider funktioniert das nicht.

Ich habe Folgendes versucht, ohne Erfolg:

  • Verwendung von Streams zur Lösung ähnlicher Probleme bei anderen Klassen, aber es funktioniert nicht mit Verzeichnissen. Der Code war ähnlich zu http://www.exampledepot.com/egs/java.io/CopyFile.html

  • Erstellen der Dateivorlage mit new File(getClass().getResource("template").toUri())

Während ich dies schrieb, dachte ich darüber nach, anstelle eines Vorlagenverzeichnisses im Ressourcenpfad eine Zip-Datei davon zu haben. Tun es auf diese Weise könnte ich die Datei als inputStream erhalten und entpacken Sie es, wo ich brauche. Aber ich bin nicht sicher, ob es der richtige Weg ist.

3voto

ChssPly76 Punkte 97241

Ich bin nicht sicher, was FileHelper ist oder tut, aber Sie werden NICHT in der Lage sein, Dateien (oder Verzeichnisse) direkt aus JAR zu kopieren. Mit InputStream, wie Sie erwähnt haben, ist der richtige Weg (entweder von jar oder 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();

Sie müssen die oben genannten Schritte für jede Ihrer Dateien durchführen (wobei Ausnahmen natürlich entsprechend behandelt werden). Es kann sein, dass Sie (je nach Ihrer Bereitstellungskonfiguration) die betreffende jar-Datei nicht als JarFile (sie ist möglicherweise nicht als tatsächliche Datei verfügbar, wenn sie z. B. als Teil einer nicht erweiterten Webanwendung bereitgestellt wird). Wenn Sie sie lesen können, sollten Sie in der Lage sein, die Liste der JarEntry-Instanzen zu durchlaufen und so Ihre Verzeichnisstruktur wiederherzustellen; andernfalls müssen Sie sie möglicherweise an anderer Stelle speichern (z. B. in einer Text- oder Xml-Ressource)

Vielleicht möchten Sie einen Blick werfen auf Commons IO Bibliothek - sie bietet eine Menge häufig verwendeter Stream-/Dateifunktionen, einschließlich Kopieren.

3voto

4F2E4A2E Punkte 1466

Hier ist eine funktionierende Version aus dem tess4j Projekt:

 /**
 * 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();
    }

}

3voto

Miguel Jiménez Punkte 1249

Ich weiß, dass diese Frage schon ziemlich alt ist, aber nachdem ich einige Antworten ausprobiert hatte, die nicht funktionierten, und andere, die eine ganze Bibliothek für nur eine Methode erforderten, beschloss ich, eine Klasse zusammenzustellen. Sie benötigt keine Bibliotheken von Drittanbietern und wurde mit Java 8 getestet. Es gibt vier öffentliche Methoden: copyResourcesToTempDir , copyResourcesToDir , copyResourceDirectory y 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();
    }

}

2voto

Vineet Reynolds Punkte 74302

Sie könnten die ClassLoader um eine Strom zur Ressource . Sobald Sie einen InputStream erhalten haben, können Sie den Inhalt des Streams auslesen und in einen OutputStream schreiben.

In Ihrem Fall müssen Sie mehrere OutputStream-Instanzen erstellen, eine für jede Datei, die Sie in den Zielordner kopieren möchten. Dies setzt natürlich voraus, dass Sie die Dateinamen im Voraus kennen.

Für diese Aufgabe ist es vorzuziehen, getResourceAsStream zu verwenden, anstatt getResource oder getResources().

2voto

galvanom Punkte 41

Ich habe vor kurzem ein ähnliches Problem gehabt. Ich habe versucht, den Ordner aus den Java-Ressourcen zu extrahieren. Also habe ich dieses Problem mit Spring gelöst PathMatchingResourcePatternResolver .

Dieser Code ruft alle Dateien und Verzeichnisse der angegebenen Ressource ab:

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                + resourceFolder + "/**");

Und dies ist die Klasse, die alle Dateien und Verzeichnisse aus der Ressource in den Festplattenpfad kopiert.

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

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X