如何解决解压报initializing SevenZipJBin

2024-01-03  本文已影响0人  爱学习的蹭蹭

1、在SevenZipJBinding官方【底层代码】里面高并发存在一个问题bug

异常如下:initializing SevenZipJBinding native library: can't copy native library out of a resource file to the temporary location
在多个文件解压的时候,文件会占用,导致多次拷贝此lib7-Zip-JBinding.dll文件导致读写的时候操作文件出错 ,本人电脑是window,不同电脑环境会操作如下图的文件

lib7-Zip-JBinding.dll

2、个人的解决方案是重写SevenZip.copyLibraryToFS方法

官方代码代码如下:

private static void copyLibraryToFS(File toLibTmpFile, InputStream fromLibInputStream) {
        FileOutputStream libTmpOutputStream = null;
        try {
            libTmpOutputStream = new FileOutputStream(toLibTmpFile);
            byte[] buffer = new byte[65536];
            while (true) {
                int read = fromLibInputStream.read(buffer);
                if (read > 0) {
                    libTmpOutputStream.write(buffer, 0, read);
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Error initializing SevenZipJBinding native library: "
                    + "can't copy native library out of a resource file to the temporary location: '"
                    + toLibTmpFile.getAbsolutePath() + "'", e);
        } finally {
            try {
                fromLibInputStream.close();
            } catch (IOException e) {
                // Ignore errors here
            }
            try {
                if (libTmpOutputStream != null) {
                    libTmpOutputStream.close();
                }
            } catch (IOException e) {
                // Ignore errors here
            }
        }
    }

3、修改后的代码如下:

官方代码无判断文件存在,导致,initializing SevenZipJBinding native library: can't copy native错误

    private static final Lock lock = new ReentrantLock();
    private static void copyLibraryToFS(File toLibTmpFile, InputStream fromLibInputStream) {
        if (!toLibTmpFile.exists()) {
            // 使用锁来确保只有一个线程执行
            lock.lock();
            FileOutputStream libTmpOutputStream = null;
            try {
                libTmpOutputStream = new FileOutputStream(toLibTmpFile);
                byte[] buffer = new byte[65536];
                while (true) {
                    int read = fromLibInputStream.read(buffer);
                    if (read > 0) {
                        libTmpOutputStream.write(buffer, 0, read);
                    } else {
                        break;
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("Error initializing SevenZipJBinding native library: "
                        + "can't copy native library out of a resource file to the temporary location: '"
                        + toLibTmpFile.getAbsolutePath() + "'", e);
            } finally {
                lock.unlock();
                try {
                    if (fromLibInputStream != null) {
                        fromLibInputStream.close();
                    }
                    if (libTmpOutputStream != null) {
                        libTmpOutputStream.close();
                    }
                } catch (IOException e) {
                    // Ignore errors here
                }
            }
        } else {
            System.out.println("toLibTmpFile exist" + toLibTmpFile.getAbsolutePath());
        }
    }

4、注意:包名保留官方的即可

在自己工程里面创建net.sf.sevenzipjbinding把SevenZip拷贝进去
调用的时候会优先执行自己工程的SevenZip代码.

package net.sf.sevenzipjbinding;

5、修改后的完整代码

package net.sf.sevenzipjbinding;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.AccessController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import net.sf.sevenzipjbinding.impl.OutArchiveImpl;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.impl.VolumedArchiveInStream;

/**
 * 7-Zip-JBinding main class.
 *
 * <ul>
 * <li>Finds and initializes 7-Zip-JBinding native library
 * <li>Opens existing archives and returns implementation of {@link IInArchive}
 * <li>Create new archives by providing different implementations of the {@link IOutArchive}
 * </ul>
 *
 * <h3>Initialization of the native library</h3>
 * <p>
 * Typically the library doesn't need an explicit initialization. The first call to an open/create archive method will
 * try to initialize the native library by calling {@link #initSevenZipFromPlatformJAR()} method. This initialization
 * process requires a platform jar to be in a class path. The automatic initialization starts before the first access to
 * an archive, if native library wasn't already initialized manually with one of the <code>initSevenZip...</code>
 * methods. If manual or automatic initialization failed, no further automatic initialization attempts will be made. The
 * initialization status and error messages can be obtained by following methods:
 * <ul>
 * <li>{@link #isInitializedSuccessfully()} - get initialization status</li>
 * <li>{@link #isAutoInitializationWillOccur()} - determine, if an initialization attempt was made</li>
 * <li>{@link #getLastInitializationException()} - get last thrown initialization exception</li>
 * </ul>
 * <br>
 * The platform jar is a additional jar file <code>sevenzipjbinding-<i>Platform</i>.jar</code> with one or more native
 * libraries for respective one or more platforms. Here is some examples of 7-Zip-JBinding platform jar files.
 * <ul>
 * <li><code>sevenzipjbinding-Linux-i386.jar</code> with native library for exact one platform: Linux, 32 bit</li>
 * <li><code>sevenzipjbinding-AllWindows.jar</code> with native libraries for two platforms: Windows 32 and 64 bit</li>
 * <li><code>sevenzipjbinding-AllPlatforms.jar</code> with native libraries for all available platforms</li>
 * </ul>
 * The single and multiple platform jar files can be determined by counting dashes in the filename. Single platform jar
 * files always contain two dashes in their names.<br>
 * <br>
 * Here is a schema of the different initialization processes:
 *
 * <ul>
 * <li>Initialization using platform jar
 * <ul>
 * <li>First, the list of the available native libraries is loaded from the
 * <code>/sevenzipjbinding-platforms.properties</code> file on the class path by calling {@link #getPlatformList()}
 * method. The list is cached in a static variable.</li>
 * <li>The platform is chosen by calling <code>getPlatformBestMatch</code> method. If the list of available platforms
 * contains exact one platform the platform will be always the best match. If more that one platforms are available to
 * choose from, the system properties <code>os.arch</code> and <code>os.name</code> (first part) are used to make the
 * choice.</li>
 * <li>The list of the native libraries is determined by reading
 * <code>/ChosenPlatform/sevenzipjbinding-lib.properties</code> on the class path. The list contains names and hashes of
 * the dynamic libraries located in the <code>/ChosenPlatform/</code> directory in the same jar.</li>
 * <li>The dynamic libraries for the chosen platform are copied to the unique temporary directory using "build-ref"
 * postfix. If not passed as a parameter for one of <code>initSevenZipFromPlatformJAR(...)</code> methods, the temporary
 * directory is determined using system property <code>java.io.tmpdir</code>.</li>
 * <li>The dynamic libraries are reused, if the files are already present in the temporary directory and the hash sums
 * are verified
 * <li>The dynamic libraries are loaded into JVM using {@link System#load(String)} method.</li>
 * <li>7-Zip-JBinding native initialization method called to complete initialization process.</li>
 * </ul>
 * <li>Manual initialization
 * <ul>
 * <li>User loads 7-Zip-JBinding native dynamic libraries manually into JVM using {@link System#load(String)} or
 * {@link System#loadLibrary(String)}</li>
 * <li>User calls {@link #initLoadedLibraries()} method to initialize manually loaded native library</li>
 * </ul>
 * </li>
 * </ul>
 *
 * <br>
 * By default the initialization occurred within the
 * {@link AccessController#doPrivileged(PrivilegedAction)} block. This can be overruled by setting
 * <code>sevenzip.no_doprivileged_initialization</code> system property. For example: <blockquote>
 * <code>java -Dsevenzip.no_doprivileged_initialization=1 ...</code> </blockquote>
 *
 * <h3>Temporary artifacts</h3>
 * <p>
 * During automatic initialization of the 7-Zip-JBinding the native libraries from the platform jar must be extracted to
 * the disk in order to be loaded into the JVM. Since the count of the native libraries (depending on the platform) can
 * be greater than one, a temporary sub-directory is created to hold those native libraries. The path to the directory
 * for the temporary artifacts will determined according to following rules (see <code>createOrVerifyTmpDir</code>
 * method):
 * <ul>
 * <li>If path specified directly using <code>tmpDirectory</code> parameter of
 * {@link #initSevenZipFromPlatformJAR(File)} or {@link #initSevenZipFromPlatformJAR(String, File)} it will be used
 * <li>If no path specified directly, the system property <code>java.io.tmpdir</code> get used
 * <li>If the system property <code>java.io.tmpdir</code> isn't set, an exception get raised
 * </ul>
 * <br>
 * The list of the temporary created artifact can be obtained with {@link #getTemporaryArtifacts()}. By default,
 * 7-Zip-JBinding doesn't delete those artifacts trying to reduce subsequent initialization overhead. If 7-Zip-JBinding
 * finds the native libraries within the temporary directory, it uses those without further verification. In order to
 * allow smoothly updates, the temporary sub-directory with the native libraries named with a unique build reference
 * number. If 7-Zip-JBinding get updated, a new temporary sub-directory get created and the new native libraries will be
 * copied and used.
 *
 * <h3>Opening existing archives</h3>
 * <p>
 * The methods for the opening archive files are
 * <ul>
 * <li>{@link #openInArchive(ArchiveFormat, IInStream)} - simple open archive method.</li>
 * <li>{@link #openInArchive(ArchiveFormat, IInStream, IArchiveOpenCallback)} - generic open archive method. It's
 * possible to open all kinds of archives providing call back object that implements following interfaces
 * <ul>
 * <li>{@link IArchiveOpenCallback} - base interface. Must be implemented by all call back classes</li>
 * <li>{@link ICryptoGetTextPassword} - (optional) Provides password encrypted index</li>
 * <li>{@link IArchiveOpenVolumeCallback} - (optional) Provides information about volumes in multipart archives.
 * Currently used only for multipart <code>RAR</code> archives. For opening multipart <code>7z</code> archives use
 * {@link VolumedArchiveInStream}.</li>
 * </ul>
 * </li>
 * <li>{@link #openInArchive(ArchiveFormat, IInStream, String)} a shortcut method for opening password protected
 * archives with an encrypted index.</li>
 * </ul>
 *
 * <h3>Creating new archives</h3>
 * <p>
 * There are two ways to create a new archive:
 * <ul>
 * <li>Use {@link SevenZip#openOutArchive(ArchiveFormat)} method. It will return an instance of the
 * {@link IOutCreateArchive}{@code <}{@link IOutItemAllFormats}{@code >} interface allowing creation of an archive of
 * any supported archive format. To get all currently supported formats see the 'compression' column of the
 * {@link ArchiveFormat} -JavaDoc.
 * <li>Use one of the <code>SevenZip.openOutArchiveXxx</code> methods, that provide implementations of corresponding
 * archive format specific interfaces. Those interfaces contain all supported configuration methods for selected archive
 * format and are more convenient in cases, where only one archive format should be supported.
 * </ul>
 * <p>
 * For more information see {@link IOutCreateArchive}.
 *
 * <h3>Updating existing archives</h3>
 * <p>
 * In order to update an existing archive three simple steps are necessary:
 * <ul>
 * <li>Open the existing archive need to be modified (getting an instance of the {@link IInArchive} interface)
 * <li>Call {@link IInArchive#getConnectedOutArchive()} to get connected instance of the {@link IOutUpdateArchive}
 * interface
 * <li>Call {@link IOutUpdateArchive#updateItems(ISequentialOutStream, int, IOutCreateCallback)} to start the archive
 * update operation
 * </ul>
 * <p>
 * During update operation user may copy item properties or item properties and content from the existing archive
 * significantly improving performance comparing to extract and re-compress alternative.<br>
 * <br>
 * For more information see {@link IOutUpdateArchive}.
 *
 * @author Boris Brodski
 * @since 4.65-1
 */
public class SevenZip {
    /**
     * Version information about 7-Zip.
     *
     * @author Boris Brodski
     * @since 9.20-2.00
     */
    public static class Version {
        /**
         * Major version of the 7-Zip engine
         */
        public int major;

        /**
         * Minor version of the 7-Zip engine
         */
        public int minor;

        /**
         * Build id of the 7-Zip engine
         */

        public int build;

        /**
         * Formatted version of the 7-Zip engine
         */
        public String version;

        /**
         * Version date
         */
        public String date;

        /**
         * copyright
         */
        public String copyright;
    }

    // Also change in /CMakeLists.txt
    private static final String SEVENZIPJBINDING_VERSION = "16.02-2.01";

    private static final String SYSTEM_PROPERTY_TMP = "java.io.tmpdir";
    private static final String SYSTEM_PROPERTY_SEVEN_ZIP_NO_DO_PRIVILEGED_INITIALIZATION = "sevenzip.no_doprivileged_initialization";
    private static final String PROPERTY_SEVENZIPJBINDING_LIB_NAME = "lib.%s.name";
    private static final String PROPERTY_SEVENZIPJBINDING_LIB_HASH = "lib.%s.hash";
    private static final String PROPERTY_BUILD_REF = "build.ref";
    private static final String SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME = "sevenzipjbinding-lib.properties";
    private static final String SEVENZIPJBINDING_PLATFORMS_PROPRETIES_FILENAME = "/sevenzipjbinding-platforms.properties";

    private static boolean autoInitializationWillOccur = true;
    private static boolean initializationSuccessful = false;
    private static SevenZipNativeInitializationException lastInitializationException = null;
    private static List<String> availablePlatforms = null;
    private static String usedPlatform = null;
    private static File[] temporaryArtifacts = null;
    private static final Lock lock = new ReentrantLock();

    /**
     * Hide default constructor
     */
    private SevenZip() {

    }

    /**
     * Tests native library initialization status of SevenZipJBinding. Use {@link #getLastInitializationException()}
     * method to get more information in case of initialization failure.
     *
     * @return <code>true</code> 7-Zip-JBinding native library was initialized successfully. Native library wasn't
     * initialized successfully (yet).
     * @see #getLastInitializationException()
     * @see #isAutoInitializationWillOccur()
     */
    public static synchronized boolean isInitializedSuccessfully() {
        return initializationSuccessful;
    }

    /**
     * Returns last native library initialization exception, if occurs.
     *
     * @return <code>null</code> - no initialization exception occurred (yet), else initialization exception
     * @see SevenZip#isInitializedSuccessfully()
     */
    public static synchronized Throwable getLastInitializationException() {
        return lastInitializationException;
    }

    /**
     * Returns weather automatic initialization will occur or not. Automatic initialization starts before opening an
     * archive, if native library wasn't already initialized manually with one of the <code>initSevenZip...</code>
     * methods. If manual or automatic initialization failed, no further automatic initialization attempts will be made.
     *
     * @return <code>true</code> automatic initialization will occur, <code>false</code> automatic initialization will
     * not occur
     * @see #isInitializedSuccessfully()
     * @see #getLastInitializationException()
     */
    public static synchronized boolean isAutoInitializationWillOccur() {
        return autoInitializationWillOccur;
    }

    /**
     * Return the platform used for the initialization. The Platform is one element out of the list of available
     * platforms returned by {@link #getPlatformList()}.
     *
     * @return the platform used for the initialization or <code>null</code> if initialization wasn't performed yet.
     * @see SevenZip#getPlatformList()
     */
    public static synchronized String getUsedPlatform() {
        return usedPlatform;
    }

    /**
     * Load list of the available platforms out of <code>sevenzipjbinding-<i>Platform</i>.jar</code> on the class path.
     *
     * @return list of the available platforms
     * @throws SevenZipNativeInitializationException indicated problems finding or parsing platform property file
     */
    public static synchronized List<String> getPlatformList() throws SevenZipNativeInitializationException {
        if (availablePlatforms != null) {
            return availablePlatforms;
        }

        InputStream propertiesInputStream = SevenZip.class
                .getResourceAsStream(SEVENZIPJBINDING_PLATFORMS_PROPRETIES_FILENAME);
        if (propertiesInputStream == null) {
            throw new SevenZipNativeInitializationException("Can not find 7-Zip-JBinding platform property file "
                    + SEVENZIPJBINDING_PLATFORMS_PROPRETIES_FILENAME
                    + ". Make sure the 'sevenzipjbinding-<Platform>.jar' file is "
                    + "on the class path or consider initializing SevenZipJBinding manualy using one of "
                    + "the offered initialization methods: 'net.sf.sevenzipjbinding.SevenZip.init*()'");
        }

        Properties properties = new Properties();
        try {
            properties.load(propertiesInputStream);
        } catch (IOException e) {
            throwInitException(e,
                    "Error loading existing property file " + SEVENZIPJBINDING_PLATFORMS_PROPRETIES_FILENAME);
        }

        List<String> platformList = new ArrayList<String>();
        for (int i = 1; ; i++) {
            String platform = properties.getProperty("platform." + i);
            if (platform == null) {
                break;
            }
            platformList.add(platform);
        }

        return availablePlatforms = platformList;
    }

    /**
     * Returns list of the temporary created artifacts (one directory and one or more files within this directory). The
     * directory is always the last element in the array.
     *
     * @return array of {@link File}s.
     */
    public static synchronized File[] getTemporaryArtifacts() {
        return temporaryArtifacts;
    }

    /**
     * Initialize native SevenZipJBinding library assuming <code>sevenzipjbinding-<i>Platform</i>.jar</code> on the
     * class path. The platform depended library will be extracted from the jar file and copied to the temporary
     * directory. Then it will be loaded into JVM using {@link System#load(String)} method. Finally the library specific
     * native initialization method will be called. Please see JavaDoc of {@link SevenZip} for detailed information.<br>
     * <br>
     * If libraries for more that one platform exists, the choice will be made by calling
     * {@link #getPlatformBestMatch()} method. Use {@link #initSevenZipFromPlatformJAR(String)} to set platform
     * manually.
     *
     * @throws SevenZipNativeInitializationException indicated problems finding a native library, coping it into the temporary directory or loading it.
     * @see SevenZip
     * @see #initSevenZipFromPlatformJAR(File)
     * @see #initSevenZipFromPlatformJAR(String)
     * @see #initSevenZipFromPlatformJAR(String, File)
     */
    public static void initSevenZipFromPlatformJAR() throws SevenZipNativeInitializationException {
        initSevenZipFromPlatformJARIntern(null, null);
    }

    /**
     * Initialize native SevenZipJBinding library assuming <code>sevenzipjbinding-<i>Platform</i>.jar</code> on the
     * class path. The platform depended library will be extracted from the jar file and copied to the temporary
     * directory. Then it will be loaded into JVM using {@link System#load(String)} method. Finally the library specific
     * native initialization method will be called. Please see JavaDoc of {@link SevenZip} for detailed information.<br>
     * <br>
     * If libraries for more that one platform exists, the choice will be made by calling
     * {@link #getPlatformBestMatch()} method. Use {@link #initSevenZipFromPlatformJAR(String)} to set platform
     * manually.
     *
     * @param tmpDirectory temporary directory to copy native libraries to. This directory must be writable and contain at least
     *                     2 MB free space.
     * @throws SevenZipNativeInitializationException indicated problems finding a native library, coping it into the temporary directory or loading it.
     * @see SevenZip
     * @see #initSevenZipFromPlatformJAR()
     * @see #initSevenZipFromPlatformJAR(String)
     * @see #initSevenZipFromPlatformJAR(String, File)
     */
    public static void initSevenZipFromPlatformJAR(File tmpDirectory) throws SevenZipNativeInitializationException {
        initSevenZipFromPlatformJARIntern(null, tmpDirectory);
    }

    /**
     * Initialize native SevenZipJBinding library assuming <code>sevenzipjbinding-<i>Platform</i>.jar</code> on the
     * class path. The platform depended library will be extracted from the jar file and copied to the temporary
     * directory. Then it will be loaded into JVM using {@link System#load(String)} method. Finally the library specific
     * native initialization method will be called. Please see JavaDoc of {@link SevenZip} for detailed information.<br>
     * <br>
     * If libraries for more that one platform exists, the choice will be made by calling
     * {@link #getPlatformBestMatch()} method. Use {@link #initSevenZipFromPlatformJAR(String)} to set platform
     * manually.
     *
     * @param tmpDirectory temporary directory to copy native libraries to. This directory must be writable and contain at least
     *                     2 MB free space.
     * @param platform     Platform to load native library for. The platform must be one of the elements of the list of available
     *                     platforms returned by {@link #getPlatformList()}.
     * @throws SevenZipNativeInitializationException indicated problems finding a native library, coping it into the temporary directory or loading it.
     * @see SevenZip
     * @see #initSevenZipFromPlatformJAR()
     * @see #initSevenZipFromPlatformJAR(File)
     * @see #initSevenZipFromPlatformJAR(String)
     * @see #getPlatformList()
     */
    public static void initSevenZipFromPlatformJAR(String platform, File tmpDirectory)
            throws SevenZipNativeInitializationException {
        initSevenZipFromPlatformJARIntern(platform, tmpDirectory);
    }

    /**
     * Initialize native SevenZipJBinding library assuming <code>sevenzipjbinding-<i>Platform</i>.jar</code> on the
     * class path. The platform depended library will be extracted from the jar file and copied to the temporary
     * directory. Then it will be loaded into JVM using {@link System#load(String)} method. Finally the library specific
     * native initialization method will be called. Please see JavaDoc of {@link SevenZip} for detailed information.<br>
     * <br>
     * If libraries for more that one platform exists, the choice will be made by calling
     * {@link #getPlatformBestMatch()} method. Use {@link #initSevenZipFromPlatformJAR(String)} to set platform
     * manually.
     *
     * @param platform Platform to load native library for. The platform must be one of the elements of the list of available
     *                 platforms returned by {@link #getPlatformList()}.
     * @throws SevenZipNativeInitializationException indicated problems finding a native library, coping it into the temporary directory or loading it.
     * @see SevenZip
     * @see #initSevenZipFromPlatformJAR()
     * @see #initSevenZipFromPlatformJAR(File)
     * @see #initSevenZipFromPlatformJAR(String, File)
     * @see #getPlatformList()
     */
    public static void initSevenZipFromPlatformJAR(String platform) throws SevenZipNativeInitializationException {
        initSevenZipFromPlatformJARIntern(platform, null);
    }

    /**
     * Perform the initialization: read platform property jar and retrieve list of available platforms. Pick best
     * platform or use chosen by parameter platform. Locate <code>sevenzipjbinding-lib.properties</code> property file,
     * read list of native libraries needed to load. Copy native library to the temporary directory passed by parameter
     * or using <code>java.io.tmpdir</code> system property. Load native libraries into JVM. Call 7-Zip-JBinding native
     * library initialization function.
     *
     * @param platform     (optional) platform to use or <code>null</code>.
     * @param tmpDirectory (optional) temporary directory to use or <code>null</code>.
     * @throws SevenZipNativeInitializationException by initialization failure
     */
    private static synchronized void initSevenZipFromPlatformJARIntern(String platform, File tmpDirectory)
            throws SevenZipNativeInitializationException {
        try {
            autoInitializationWillOccur = false;
            if (initializationSuccessful) {
                // Native library was already initialized successfully. No need for further initialization.
                return;
            }

            determineAndSetUsedPlatform(platform);
            Properties properties = loadSevenZipJBindingLibProperties();
            File tmpDirFile = createOrVerifyTmpDir(tmpDirectory);
            File sevenZipJBindingTmpDir = getOrCreateSevenZipJBindingTmpDir(tmpDirFile, properties);
            List<File> nativeLibraries = copyOrSkipLibraries(properties, sevenZipJBindingTmpDir);
            loadNativeLibraries(nativeLibraries);
            nativeInitialization();
        } catch (SevenZipNativeInitializationException sevenZipNativeInitializationException) {
            lastInitializationException = sevenZipNativeInitializationException;
            throw sevenZipNativeInitializationException;
        }
    }

    private static void determineAndSetUsedPlatform(String platform) throws SevenZipNativeInitializationException {
        if (platform == null) {
            usedPlatform = getPlatformBestMatch();
        } else {
            usedPlatform = platform;
        }
    }

    private static Properties loadSevenZipJBindingLibProperties() throws SevenZipNativeInitializationException {
        String pathInJAR = "/" + usedPlatform + "/";

        // Load 'sevenzipjbinding-lib.properties'
        InputStream sevenZipJBindingLibProperties = SevenZip.class
                .getResourceAsStream(pathInJAR + SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME);
        if (sevenZipJBindingLibProperties == null) {
            throwInitException("error loading property file '" + pathInJAR + SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME
                    + "' from a jar-file 'sevenzipjbinding-<Platform>.jar'. Is the platform jar-file not on the class path?");
        }

        Properties properties = new Properties();
        try {
            properties.load(sevenZipJBindingLibProperties);
        } catch (IOException e) {
            throwInitException("error loading property file '" + SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME
                    + "' from a jar-file 'sevenzipjbinding-<Platform>.jar'");
        }
        return properties;
    }

    private static File createOrVerifyTmpDir(File tmpDirectory) throws SevenZipNativeInitializationException {
        File tmpDirFile;
        if (tmpDirectory != null) {
            tmpDirFile = tmpDirectory;
        } else {
            String systemPropertyTmp = System.getProperty(SYSTEM_PROPERTY_TMP);
            if (systemPropertyTmp == null) {
                throwInitException("can't determinte tmp directory. Use may use -D" + SYSTEM_PROPERTY_TMP
                        + "=<path to tmp dir> parameter for jvm to fix this.");
            }
            tmpDirFile = new File(systemPropertyTmp);
        }

        if (!tmpDirFile.exists() || !tmpDirFile.isDirectory()) {
            throwInitException("invalid tmp directory '" + tmpDirectory + "'");
        }

        if (!tmpDirFile.canWrite()) {
            throwInitException("can't create files in '" + tmpDirFile.getAbsolutePath() + "'");
        }
        return tmpDirFile;
    }

    private static File getOrCreateSevenZipJBindingTmpDir(File tmpDirFile, Properties properties)
            throws SevenZipNativeInitializationException {
        String buildRef = getOrGenerateBuildRef(properties);
        File tmpSubdirFile = new File(tmpDirFile.getAbsolutePath() + File.separator + "SevenZipJBinding-" + buildRef);
        if (!tmpSubdirFile.exists()) {
            if (!tmpSubdirFile.mkdir()) {
                throwInitException("Directory '" + tmpDirFile.getAbsolutePath() + "' couldn't be created");
            }
        }
        return tmpSubdirFile;
    }

    private static String getOrGenerateBuildRef(Properties properties) {
        String buildRef = properties.getProperty(PROPERTY_BUILD_REF);
        if (buildRef == null) {
            buildRef = Integer.toString(new Random().nextInt(10000000));
        }
        return buildRef;
    }

    private static List<File> copyOrSkipLibraries(Properties properties, File sevenZipJBindingTmpDir)
            throws SevenZipNativeInitializationException {
        List<File> nativeLibraries = new ArrayList<File>(5);
        for (int i = 1; ; i++) {
            String propertyName = String.format(PROPERTY_SEVENZIPJBINDING_LIB_NAME, Integer.valueOf(i));
            String propertyHash = String.format(PROPERTY_SEVENZIPJBINDING_LIB_HASH, Integer.valueOf(i));
            String libName = properties.getProperty(propertyName);
            String libHash = properties.getProperty(propertyHash);
            if (libName == null) {
                if (nativeLibraries.size() == 0) {
                    throwInitException("property file '" + SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME
                            + "' from 'sevenzipjbinding-<Platform>.jar' missing property '" + propertyName + "'");
                } else {
                    break;
                }
            }
            if (libHash == null) {
                throwInitException("property file '" + SEVENZIPJBINDING_LIB_PROPERTIES_FILENAME
                        + "' from 'sevenzipjbinding-<Platform>.jar' missing property " + propertyHash
                        + " containing the hash for the library '" + libName + "'");
            }
            File libTmpFile = new File(sevenZipJBindingTmpDir.getAbsolutePath() + File.separatorChar + libName);

            if (!libTmpFile.exists() || !hashMatched(libTmpFile, libHash)) {
                InputStream libInputStream = SevenZip.class.getResourceAsStream("/" + usedPlatform + "/" + libName);
                if (libInputStream == null) {
                    throwInitException("error loading native library '" + libName
                            + "' from a jar-file 'sevenzipjbinding-<Platform>.jar'.");
                }

                copyLibraryToFS(libTmpFile, libInputStream);
            }
            nativeLibraries.add(libTmpFile);
        }
        applyTemporaryArtifacts(sevenZipJBindingTmpDir, nativeLibraries);
        return nativeLibraries;
    }

    private static boolean hashMatched(File libTmpFile, String libHash) throws SevenZipNativeInitializationException {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA1");
        } catch (NoSuchAlgorithmException e) {
            throwInitException(e, "Error initializing SHA1 algorithm");
            return false;
        }

        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(libTmpFile);
        } catch (IOException e) {
            throwInitException(e, "Error opening library file from the temp directory for reading: '"//
                    + libTmpFile.getAbsolutePath() + "'");
            return false;
        }

        boolean ok = false;
        try {
            byte[] buffer = new byte[128 * 1024];
            while (true) {
                int length;
                try {
                    length = fileInputStream.read(buffer);
                } catch (IOException e) {
                    throwInitException(e, "Error reading from library file opened from the temp directory: '"
                            + libTmpFile.getAbsolutePath() + "'");
                    return false;
                }
                if (length <= 0) {
                    break;
                }
                digest.update(buffer, 0, length);
            }

            boolean result = byteArrayToHex(digest.digest()).equals(libHash.trim().toLowerCase());

            ok = true;
            return result;
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    if (ok) {
                        throwInitException(e,
                                "Error closing library file from the temp directory (opened for reading): '"
                                        + libTmpFile.getAbsolutePath() + "'");
                    }
                }
            }
        }
    }

    private static String byteArrayToHex(byte[] byteArray) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < byteArray.length; i++) {
            stringBuilder.append(Integer.toHexString(0xFF & byteArray[i]));
        }
        return stringBuilder.toString();
    }

    private static void applyTemporaryArtifacts(File sevenZipJBindingTmpDir, List<File> nativeLibraries) {
        temporaryArtifacts = new File[nativeLibraries.size() + 1];
        nativeLibraries.toArray(temporaryArtifacts);
        temporaryArtifacts[temporaryArtifacts.length - 1] = sevenZipJBindingTmpDir;
    }

    private static void loadNativeLibraries(List<File> libraryList) throws SevenZipNativeInitializationException {
        // Load native libraries in to reverse order
        for (int i = libraryList.size() - 1; i != -1; i--) {
            String libraryFileName = libraryList.get(i).getAbsolutePath();
            try {
                System.load(libraryFileName);
            } catch (Throwable t) {
                throw new SevenZipNativeInitializationException(
                        "7-Zip-JBinding initialization failed: Error loading native library: '" + libraryFileName + "'",
                        t);
            }
        }
    }

    /**
     * Initialize 7-Zip-JBinding native library without loading libraries in JVM first. Prevent automatic loading of
     * 7-Zip-JBinding native libraries into JVM. This method will only call 7-Zip-JBinding internal initialization
     * method, considering all needed native libraries as loaded. It method is useful, if the java application wants to
     * load 7-Zip-JBinding native libraries manually.
     *
     * @throws SevenZipNativeInitializationException in case of an initialization error
     */
    public static synchronized void initLoadedLibraries() throws SevenZipNativeInitializationException {
        if (initializationSuccessful) {
            return;
        }
        autoInitializationWillOccur = false;
        nativeInitialization();
    }

    private static void nativeInitialization() throws SevenZipNativeInitializationException {
        String doPrivileged = System.getProperty(SYSTEM_PROPERTY_SEVEN_ZIP_NO_DO_PRIVILEGED_INITIALIZATION);
        final String errorMessage[] = new String[1];
        final Throwable throwable[] = new Throwable[1];
        if (doPrivileged == null || doPrivileged.trim().equals("0")) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        errorMessage[0] = nativeInitSevenZipLibrary();
                    } catch (Throwable e) {
                        throwable[0] = e;
                    }
                    return null;
                }
            });
        } else {
            errorMessage[0] = nativeInitSevenZipLibrary();
        }
        if (errorMessage[0] != null || throwable[0] != null) {
            String message = errorMessage[0];
            if (message == null) {
                message = "No message";
            }
            lastInitializationException = new SevenZipNativeInitializationException(
                    "Error initializing 7-Zip-JBinding: " + message, throwable[0]);
            throw lastInitializationException;
        }
        initializationSuccessful = true;
    }

    /**
     * Open archive of type <code>archiveFormat</code> from the input stream <code>inStream</code> using 'archive open
     * call back' listener <code>archiveOpenCallback</code>. To open archive from the file, use
     * {@link RandomAccessFileInStream}.
     *
     * @param archiveFormat       format of archive
     * @param inStream            input stream to open archive from
     * @param archiveOpenCallback archive open call back listener to use. You can optionally implement {@link ICryptoGetTextPassword} to
     *                            specify password to use.
     * @return implementation of {@link IInArchive} which represents opened archive.
     * @throws SevenZipException    7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                              stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @throws NullPointerException is thrown, if inStream is null
     * @see #openInArchive(ArchiveFormat, IInStream, IArchiveOpenCallback)
     * @see #openInArchive(ArchiveFormat, IInStream, String)
     */
    public static IInArchive openInArchive(ArchiveFormat archiveFormat, IInStream inStream,
                                           IArchiveOpenCallback archiveOpenCallback) throws SevenZipException {
        ensureLibraryIsInitialized();
        IArchiveOpenCallback openArchiveCallbackToUse = archiveOpenCallback;
        if (openArchiveCallbackToUse == null) {
            // TODO Test this!
            openArchiveCallbackToUse = new DummyOpenArchiveCallback();
        }
        if (archiveFormat != null) {
            return callNativeOpenArchive(archiveFormat, inStream, openArchiveCallbackToUse);
        }
        return callNativeOpenArchive(null, inStream, openArchiveCallbackToUse);
    }

    /**
     * Open archive of type <code>archiveFormat</code> from the input stream <code>inStream</code> using 'archive open
     * call-back' listener <code>archiveOpenCallback</code>. To open archive from the file, use
     * {@link RandomAccessFileInStream}.
     *
     * @param archiveFormat   format of archive
     * @param inStream        input stream to open archive from
     * @param passwordForOpen password to use. Warning: this password will not be used to extract item from archive but only to open
     *                        archive. (7-zip format supports encrypted filename)
     * @return implementation of {@link IInArchive} which represents opened archive.
     * @throws SevenZipException    7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                              stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @throws NullPointerException is thrown, if inStream is null
     * @see #openInArchive(ArchiveFormat, IInStream)
     * @see #openInArchive(ArchiveFormat, IInStream, IArchiveOpenCallback)
     */
    public static IInArchive openInArchive(ArchiveFormat archiveFormat, IInStream inStream, String passwordForOpen)
            throws SevenZipException {
        ensureLibraryIsInitialized();
        if (passwordForOpen == null) {
            return openInArchive(archiveFormat, inStream);
        }
        return callNativeOpenArchive(archiveFormat, inStream, new ArchiveOpenCryptoCallback(passwordForOpen));
    }

    /**
     * Open archive of type <code>archiveFormat</code> from the input stream <code>inStream</code>. To open archive from
     * the file, use {@link RandomAccessFileInStream}.
     *
     * @param archiveFormat (optional) format of archive. If <code>null</code> archive format will be auto-detected.
     * @param inStream      input stream to open archive from
     * @return implementation of {@link IInArchive} which represents opened archive.
     * @throws SevenZipException    7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                              stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @throws NullPointerException is thrown, if inStream is null
     * @see #openInArchive(ArchiveFormat, IInStream)
     * @see #openInArchive(ArchiveFormat, IInStream, String)
     */
    public static IInArchive openInArchive(ArchiveFormat archiveFormat, IInStream inStream) throws SevenZipException {
        ensureLibraryIsInitialized();
        return callNativeOpenArchive(archiveFormat, inStream, new DummyOpenArchiveCallback());
    }

    private static void ensureLibraryIsInitialized() {
        if (autoInitializationWillOccur) {
            autoInitializationWillOccur = false;
            try {
                initSevenZipFromPlatformJAR();
            } catch (SevenZipNativeInitializationException exception) {
                lastInitializationException = exception;
                throw new RuntimeException(
                        "SevenZipJBinding couldn't be initialized automaticly using initialization "
                                + "from platform depended JAR and the default temporary directory. Please, "
                                + "make sure the correct 'sevenzipjbinding-<Platform>.jar' file is "
                                + "on the class path or consider initializing SevenZipJBinding manualy using one of "
                                + "the offered initialization methods: 'net.sf.sevenzipjbinding.SevenZip.init*()'",
                        exception);
            }
        }
        if (!initializationSuccessful) {
            throw new RuntimeException("SevenZipJBinding wasn't initialized successfully last time.",
                    lastInitializationException);
        }
    }

    private static void throwInitException(String message) throws SevenZipNativeInitializationException {
        throwInitException(null, message);
    }

    private static void throwInitException(Exception exception, String message)
            throws SevenZipNativeInitializationException {
        throw new SevenZipNativeInitializationException("Error loading SevenZipJBinding native library into JVM: "
                + message + " [You may also try different SevenZipJBinding initialization methods "
                + "'net.sf.sevenzipjbinding.SevenZip.init*()' in order to solve this problem] ", exception);
    }

    private static void copyLibraryToFS(File toLibTmpFile, InputStream fromLibInputStream) {
        if (!toLibTmpFile.exists()) {
            // 使用锁来确保只有一个线程执行
            lock.lock();
            FileOutputStream libTmpOutputStream = null;
            try {
                libTmpOutputStream = new FileOutputStream(toLibTmpFile);
                byte[] buffer = new byte[65536];
                while (true) {
                    int read = fromLibInputStream.read(buffer);
                    if (read > 0) {
                        libTmpOutputStream.write(buffer, 0, read);
                    } else {
                        break;
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("Error initializing SevenZipJBinding native library: "
                        + "can't copy native library out of a resource file to the temporary location: '"
                        + toLibTmpFile.getAbsolutePath() + "'", e);
            } finally {
                lock.unlock();
                try {
                    if (fromLibInputStream != null) {
                        fromLibInputStream.close();
                    }
                    if (libTmpOutputStream != null) {
                        libTmpOutputStream.close();
                    }
                } catch (IOException e) {
                    // Ignore errors here
                }
            }
        } else {
            System.out.println("toLibTmpFile exist" + toLibTmpFile.getAbsolutePath());
        }
    }

    /**
     * Return best match for the current platform out of available platforms <code>availablePlatform</code>
     *
     * @return platform
     * @throws SevenZipNativeInitializationException is no platform could be chosen
     * @see #getPlatformList()
     */
    public static String getPlatformBestMatch() throws SevenZipNativeInitializationException {
        List<String> availablePlatform = getPlatformList();
        if (availablePlatform.size() == 1) {
            return availablePlatform.get(0);
        }

        String arch = System.getProperty("os.arch");
        String system = System.getProperty("os.name").split(" ")[0];
        if (availablePlatform.contains(system + "-" + arch)) {
            return system + "-" + arch;
        }

        // TODO allow fuzzy matches
        StringBuilder stringBuilder = new StringBuilder("Can't find suited platform for os.arch=");
        stringBuilder.append(arch);
        stringBuilder.append(", os.name=");
        stringBuilder.append(system);
        stringBuilder.append("... Available list of platforms: ");

        for (String platform : availablePlatform) {
            stringBuilder.append(platform);
            stringBuilder.append(", ");
        }
        stringBuilder.setLength(stringBuilder.length() - 2);
        throwInitException(stringBuilder.toString());
        return null; // Will never happen
    }

    private static IInArchive callNativeOpenArchive(ArchiveFormat archiveFormat, IInStream inStream,
                                                    IArchiveOpenCallback archiveOpenCallback) throws SevenZipException {
        if (inStream == null) {
            throw new NullPointerException("SevenZip.callNativeOpenArchive(...): inStream parameter is null");
        }
        return nativeOpenArchive(archiveFormat, inStream, archiveOpenCallback);
    }

    private static native IInArchive nativeOpenArchive(ArchiveFormat archiveFormat, IInStream inStream,
                                                       IArchiveOpenCallback archiveOpenCallback) throws SevenZipException;

    private static native void nativeCreateArchive(OutArchiveImpl<?> outArchiveImpl, ArchiveFormat archiveFormat)
            throws SevenZipException;

    private static native String nativeInitSevenZipLibrary() throws SevenZipNativeInitializationException;

    private static native int nativeGetVersionMajor();

    private static native int nativeGetVersionMinor();

    private static native int nativeGetVersionBuild();

    private static native String nativeGetVersionVersion();

    private static native String nativeGetVersionDate();

    private static native String nativeGetVersionCopyright();

    /**
     * Return information about native 7-Zip engine.
     *
     * @return Version
     */
    public static Version getSevenZipVersion() {
        ensureLibraryIsInitialized();

        Version version = new Version();

        version.major = nativeGetVersionMajor();
        version.minor = nativeGetVersionMinor();
        version.build = nativeGetVersionBuild();
        version.version = nativeGetVersionVersion();
        version.date = nativeGetVersionDate();
        version.copyright = nativeGetVersionCopyright();

        return version;
    }

    /**
     * Return version of the 7-Zip-JBinding.
     *
     * @return version of the 7-Zip-JBinding
     */
    public static String getSevenZipJBindingVersion() {
        return SEVENZIPJBINDING_VERSION;
    }

    /**
     * Create a new Zip archive.
     *
     * @return an out-archive object initialized to create the new Zip archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchiveZip
     */
    public static IOutCreateArchiveZip openOutArchiveZip() throws SevenZipException {
        return (IOutCreateArchiveZip) openOutArchiveIntern(ArchiveFormat.ZIP);
    }

    /**
     * Create a new 7z archive.
     *
     * @return an out-archive object initialized to create the new 7z archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchive7z
     */
    public static IOutCreateArchive7z openOutArchive7z() throws SevenZipException {
        return (IOutCreateArchive7z) openOutArchiveIntern(ArchiveFormat.SEVEN_ZIP);
    }

    /**
     * Create a new 7z archive.
     *
     * @return an out-archive object initialized to create the new 7z archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchiveTar
     */
    public static IOutCreateArchiveTar openOutArchiveTar() throws SevenZipException {
        return (IOutCreateArchiveTar) openOutArchiveIntern(ArchiveFormat.TAR);
    }

    /**
     * Create a new BZip2 archive.
     *
     * @return an out-archive object initialized to create the new BZip2 archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchiveBZip2
     */
    public static IOutCreateArchiveBZip2 openOutArchiveBZip2() throws SevenZipException {
        return (IOutCreateArchiveBZip2) openOutArchiveIntern(ArchiveFormat.BZIP2);
    }

    /**
     * Create a new GZip archive.
     *
     * @return an out-archive object initialized to create the new GZip archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchiveGZip
     */
    public static IOutCreateArchiveGZip openOutArchiveGZip() throws SevenZipException {
        return (IOutCreateArchiveGZip) openOutArchiveIntern(ArchiveFormat.GZIP);
    }

    /**
     * Create a new archive of type <code>archiveFormat</code>.
     *
     * @param archiveFormat archive format of the new archive
     * @return an out-archive object initialized to create the new archive
     * @throws SevenZipException 7-Zip or 7-Zip-JBinding error occur. Use {@link SevenZipException#printStackTraceExtended()} to get
     *                           stack traces of this SevenZipException and of the all thrown 'cause by' exceptions.
     * @see IOutCreateArchiveZip
     */
    @SuppressWarnings("unchecked")
    public static IOutCreateArchive<IOutItemAllFormats> openOutArchive(ArchiveFormat archiveFormat)
            throws SevenZipException {
        return (IOutCreateArchive<IOutItemAllFormats>) openOutArchiveIntern(archiveFormat);
    }

    private static OutArchiveImpl<?> openOutArchiveIntern(ArchiveFormat archiveFormat) throws SevenZipException {
        ensureLibraryIsInitialized();
        if (!archiveFormat.isOutArchiveSupported()) {
            throw new IllegalStateException("Archive format '" + archiveFormat + "' doesn't support archive creation.");
        }

        OutArchiveImpl<?> outArchiveImpl;
        try {
            outArchiveImpl = archiveFormat.getOutArchiveImplementation().newInstance();
        } catch (Exception e) {
            throw new IllegalStateException("Internal error: Can't create new instance of the class "
                    + archiveFormat.getOutArchiveImplementation() + " using default constructor.");
        }

        nativeCreateArchive(outArchiveImpl, archiveFormat);
        return outArchiveImpl;
    }

    private static class DummyOpenArchiveCallback implements IArchiveOpenCallback, ICryptoGetTextPassword {
        /**
         * {@inheritDoc}
         */

        public void setCompleted(Long files, Long bytes) {
        }

        /**
         * {@inheritDoc}
         */

        public void setTotal(Long files, Long bytes) {
        }

        /**
         * {@inheritDoc}
         */

        public String cryptoGetTextPassword() throws SevenZipException {
            throw new SevenZipException("No password was provided for opening protected archive.");
        }
    }

    private static final class ArchiveOpenCryptoCallback implements IArchiveOpenCallback, ICryptoGetTextPassword {
        private final String passwordForOpen;

        public ArchiveOpenCryptoCallback(String passwordForOpen) {
            this.passwordForOpen = passwordForOpen;
        }

        public void setCompleted(Long files, Long bytes) {
        }

        public void setTotal(Long files, Long bytes) {
        }

        public String cryptoGetTextPassword() throws SevenZipException {
            return passwordForOpen;
        }
    }
}

6、个人总结

以上是个人遇到的问题与解决方案,如有问题请指出,希望能帮到有需要的人。

上一篇下一篇

猜你喜欢

热点阅读