Fixed logging spaces and tabs missing, unclickable version item, removing 'logging' element when installing forge and liteloader

This commit is contained in:
huangyuhui
2017-08-25 13:22:10 +08:00
parent e70226afba
commit 3b4359f7eb
16 changed files with 96 additions and 118 deletions

View File

@@ -51,9 +51,9 @@ import com.sun.javafx.scene.layout.region.BorderStyleConverter.HIDDEN
*/ */
@Throws(IOException::class, JsonParseException::class) @Throws(IOException::class, JsonParseException::class)
fun readHMCLModpackManifest(f: File): Modpack { fun readHMCLModpackManifest(f: File): Modpack {
val manifestJson = readTextFromZipFile(f, "modpack.json") val manifestJson = f.readTextZipEntry("modpack.json")
val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.") val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.")
val gameJson = readTextFromZipFile(f, "minecraft/pack.json") val gameJson = f.readTextZipEntry("minecraft/pack.json")
val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.") val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.")
return if (game.jar == null) return if (game.jar == null)
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.") if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
@@ -71,7 +71,7 @@ class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, privat
init { init {
check(!repository.hasVersion(name), { "Version $name already exists." }) check(!repository.hasVersion(name), { "Version $name already exists." })
val json = readTextFromZipFile(zipFile, "minecraft/pack.json") val json = zipFile.readTextZipEntry("minecraft/pack.json")
var version = GSON.fromJson<Version>(json)!! var version = GSON.fromJson<Version>(json)!!
version = version.copy(jar = null) version = version.copy(jar = null)
dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync() dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync()
@@ -81,10 +81,7 @@ class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, privat
private val run = repository.getRunDirectory(name) private val run = repository.getRunDirectory(name)
override fun execute() { override fun execute() {
unzipSubDirectory(zipFile, run, "minecraft/", false) zipFile.uncompressTo(run, "minecraft/", callback = { it != "minecraft/pack.json" }, ignoreExistentFile = false)
val json = run.resolve("pack.json")
if (repository.getVersionJson(name) != json)
json.delete()
} }
} }

View File

@@ -171,7 +171,10 @@ object Settings {
else { else {
System.setProperty("http.proxyHost", proxyHost) System.setProperty("http.proxyHost", proxyHost)
System.setProperty("http.proxyPort", proxyPort) System.setProperty("http.proxyPort", proxyPort)
proxy = Proxy(proxyType, InetSocketAddress(host, port)) if (proxyType == Proxy.Type.DIRECT)
proxy = Proxy.NO_PROXY
else
proxy = Proxy(proxyType, InetSocketAddress(host, port))
val user = proxyUser val user = proxyUser
val pass = proxyPass val pass = proxyPass

View File

@@ -30,13 +30,11 @@ import javafx.scene.layout.StackPane
import javafx.scene.web.WebEngine import javafx.scene.web.WebEngine
import javafx.scene.web.WebView import javafx.scene.web.WebView
import javafx.stage.Stage import javafx.stage.Stage
import netscape.javascript.JSObject
import org.jackhuang.hmcl.game.LauncherHelper import org.jackhuang.hmcl.game.LauncherHelper
import org.jackhuang.hmcl.i18n import org.jackhuang.hmcl.i18n
import org.jackhuang.hmcl.setting.Settings import org.jackhuang.hmcl.setting.Settings
import org.jackhuang.hmcl.util.Log4jLevel import org.jackhuang.hmcl.util.*
import org.jackhuang.hmcl.util.inc
import org.jackhuang.hmcl.util.onChange
import org.jackhuang.hmcl.util.readFullyAsString
import org.w3c.dom.Document import org.w3c.dom.Document
import org.w3c.dom.Node import org.w3c.dom.Node
import java.util.concurrent.Callable import java.util.concurrent.Callable
@@ -59,7 +57,10 @@ class LogWindow : Stage() {
fun logLine(line: String, level: Log4jLevel) { fun logLine(line: String, level: Log4jLevel) {
impl.body.appendChild(impl.engine.document.createElement("div").apply { impl.body.appendChild(impl.engine.document.createElement("div").apply {
textContent = line // a <pre> element to prevent multiple spaces and tabs being removed.
appendChild(impl.engine.document.createElement("pre").apply {
textContent = line
})
}) })
impl.engine.executeScript("checkNewLog(\"${level.name.toLowerCase()}\");scrollToBottom();") impl.engine.executeScript("checkNewLog(\"${level.name.toLowerCase()}\");scrollToBottom();")

View File

@@ -72,7 +72,7 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
lblName.text = manifest!!.name lblName.text = manifest!!.name
lblVersion.text = manifest!!.version lblVersion.text = manifest!!.version
lblAuthor.text = manifest!!.author lblAuthor.text = manifest!!.author
txtModpackName.text = manifest!!.name + "-" + manifest!!.version txtModpackName.text = manifest!!.name + (if (manifest!!.version.isNullOrBlank()) "" else ("-" + manifest!!.version))
} catch (e: Exception) { } catch (e: Exception) {
// TODO // TODO
txtModpackName.text = i18n("modpack.task.install.error") txtModpackName.text = i18n("modpack.task.install.error")

View File

@@ -5,14 +5,13 @@
<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.ScrollPane?>
<fx:root <fx:root
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
styleClass="transparent" type="StackPane" styleClass="transparent" type="StackPane" pickOnBounds="false"
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1"> xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER"> <ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160"> <JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane> </JFXMasonryPane>
</ScrollPane> </ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT"> <VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<!--JFXSpinner fx:id="spinner" style="-fx-radius:10" styleClass="materialDesign-purple, first-spinner" /-->
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh" <JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh"
style="-fx-background-color:#5264AE;-fx-background-radius: 50px;"> style="-fx-background-color:#5264AE;-fx-background-radius: 50px;">
<graphic> <graphic>

View File

@@ -10,9 +10,9 @@
<?import com.jfoenix.controls.JFXRadioButton?> <?import com.jfoenix.controls.JFXRadioButton?>
<fx:root xmlns="http://javafx.com/javafx" <fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
type="StackPane"> type="StackPane" pickOnBounds="false">
<VBox fx:id="content"> <VBox fx:id="content" pickOnBounds="false">
<StackPane fx:id="header" VBox.vgrow="ALWAYS"> <StackPane fx:id="header" VBox.vgrow="ALWAYS" pickOnBounds="false">
<BorderPane> <BorderPane>
<top> <top>
<HBox alignment="CENTER_RIGHT"> <HBox alignment="CENTER_RIGHT">
@@ -26,7 +26,7 @@
</center> </center>
</BorderPane> </BorderPane>
</StackPane> </StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40"> <StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40" pickOnBounds="false">
<BorderPane> <BorderPane>
<left> <left>
<HBox spacing="8"> <HBox spacing="8">

View File

@@ -9,7 +9,7 @@
overflow-x: hidden; overflow-x: hidden;
} }
div { div > pre {
font: ${FONT}; font: ${FONT};
margin: 0px; margin: 0px;
padding: 2px; padding: 2px;

View File

@@ -43,10 +43,10 @@ object BMCLAPIDownloadProvider : DownloadProvider() {
} }
override fun injectURL(baseURL: String): String = baseURL override fun injectURL(baseURL: String): String = baseURL
.replace("https://launchermeta.mojang.com", "http://bmclapi2.bangbang93.com") .replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
.replace("https://launcher.mojang.com", "http://bmclapi2.bangbang93.com") .replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com")
.replace("https://libraries.minecraft.net", "http://bmclapi2.bangbang93.com/libraries") .replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
.replace("http://files.minecraftforge.net/maven", "http://bmclapi2.bangbang93.com/maven") .replace("http://files.minecraftforge.net/maven", "https://bmclapi2.bangbang93.com/maven")
.replace("http://dl.liteloader.com/versions/versions.json", "http://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json") .replace("http://dl.liteloader.com/versions/versions.json", "httsp://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
.replace("http://dl.liteloader.com/versions", "http://bmclapi2.bangbang93.com/maven") .replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven")
} }

View File

@@ -46,8 +46,6 @@ object MojangDownloadProvider : DownloadProvider() {
override fun injectURL(baseURL: String): String { override fun injectURL(baseURL: String): String {
if (baseURL.endsWith("net/minecraftforge/forge/json")) if (baseURL.endsWith("net/minecraftforge/forge/json"))
return baseURL return baseURL
else if (Locale.getDefault() == Locale.CHINA)
return baseURL.replace("http://files.minecraftforge.net/maven", "http://maven.aliyun.com/nexus/content/groups/public");
else else
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven") return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven")
} }

View File

@@ -73,7 +73,7 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
val versionProvider = SimpleVersionProvider() val versionProvider = SimpleVersionProvider()
versionProvider.addVersion(version) versionProvider.addVersion(version)
result = installProfile.versionInfo!!.copy(inheritsFrom = version.id).resolve(versionProvider).copy(id = version.id) result = installProfile.versionInfo!!.copy(inheritsFrom = version.id).resolve(versionProvider).copy(id = version.id, logging = emptyMap())
dependencies += GameLibrariesTask(dependencyManager, installProfile.versionInfo) dependencies += GameLibrariesTask(dependencyManager, installProfile.versionInfo)
} }

View File

@@ -69,7 +69,8 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
result = version.copy( result = version.copy(
mainClass = "net.minecraft.launchwrapper.Launch", mainClass = "net.minecraft.launchwrapper.Launch",
minecraftArguments = version.minecraftArguments + " --tweakClass " + remote.tag.tweakClass, minecraftArguments = version.minecraftArguments + " --tweakClass " + remote.tag.tweakClass,
libraries = merge(tempVersion.libraries, version.libraries) libraries = merge(tempVersion.libraries, version.libraries),
logging = emptyMap()
) )
dependencies += GameLibrariesTask(dependencyManager, tempVersion) dependencies += GameLibrariesTask(dependencyManager, tempVersion)
} }

View File

@@ -73,7 +73,7 @@ open class Library(
companion object LibrarySerializer : JsonDeserializer<Library>, JsonSerializer<Library> { companion object LibrarySerializer : JsonDeserializer<Library>, JsonSerializer<Library> {
fun fromName(name: String, url: String? = null, downloads: LibrariesDownloadInfo? = null, extract: ExtractRules? = null, natives: Map<OS, String>? = null, rules: List<CompatibilityRule>? = null): Library { fun fromName(name: String, url: String? = null, downloads: LibrariesDownloadInfo? = null, extract: ExtractRules? = null, natives: Map<OS, String>? = null, rules: List<CompatibilityRule>? = null): Library {
val arr = name.split(":".toRegex(), 3) val arr = name.split(":".toRegex(), 4)
if (arr.size != 3 && arr.size != 4) if (arr.size != 3 && arr.size != 4)
throw IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version.") throw IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version.")
return Library( return Library(

View File

@@ -214,11 +214,11 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
open fun decompressNatives() { open fun decompressNatives() {
version.libraries.filter { it.isNative }.forEach { library -> version.libraries.filter { it.isNative }.forEach { library ->
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION") @Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
unzip(zip = repository.getLibraryFile(version, library), repository.getLibraryFile(version, library).uncompressTo(
dest = native, dest = native,
callback = if (library.extract == null) null callback = if (library.extract == null) null
else library.extract!!::shouldExtract, else library.extract!!::shouldExtract,
ignoreExistsFile = false) ignoreExistentFile = false)
} }
} }
@@ -230,10 +230,13 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
decompressNatives() decompressNatives()
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) { if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
val process = Runtime.getRuntime().exec(options.precalledCommand) try {
ignoreException { val process = Runtime.getRuntime().exec(options.precalledCommand)
if (process.isAlive) if (process.isAlive)
process.waitFor() process.waitFor()
} catch (e: IOException) {
// TODO: alert precalledCommand is wrong.
// rethrow InterruptedException
} }
} }
@@ -287,6 +290,14 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
} }
private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) { private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
val enablesLoggingInfo = version.logging?.containsKey(DownloadType.CLIENT) ?: false
if (enablesLoggingInfo)
startMonitorsWithLoggingInfo(managedProcess, processListener, isDaemon)
else
startMonitorsWithoutLoggingInfo(managedProcess, processListener, isDaemon)
}
private fun startMonitorsWithLoggingInfo(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
processListener.setProcess(managedProcess) processListener.setProcess(managedProcess)
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() } val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() }
managedProcess.relatedThreads += logHandler managedProcess.relatedThreads += logHandler
@@ -297,6 +308,15 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run) managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run)
} }
private fun startMonitorsWithoutLoggingInfo(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
processListener.setProcess(managedProcess)
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.inputStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.guessLevel(it) ?: Log4jLevel.INFO); managedProcess.lines += it })::run)
managedProcess.relatedThreads += stdout
val stderr = thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.errorStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.ERROR); managedProcess.lines += it })::run)
managedProcess.relatedThreads += stderr
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> processListener.onExit(exitCode, exitType) })::run)
}
companion object { companion object {
const val LAUNCH_ASYNC_ID = "process" const val LAUNCH_ASYNC_ID = "process"
} }

View File

@@ -106,7 +106,7 @@ class CurseForgeModpackManifestFile (
*/ */
@Throws(IOException::class, JsonParseException::class) @Throws(IOException::class, JsonParseException::class)
fun readCurseForgeModpackManifest(f: File): Modpack { fun readCurseForgeModpackManifest(f: File): Modpack {
val json = readTextFromZipFile(f, "manifest.json") val json = f.readTextZipEntry("manifest.json")
val manifest = GSON.fromJson<CurseForgeModpackManifest>(json) val manifest = GSON.fromJson<CurseForgeModpackManifest>(json)
?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.") ?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
return Modpack( return Modpack(
@@ -114,7 +114,7 @@ fun readCurseForgeModpackManifest(f: File): Modpack {
version = manifest.version, version = manifest.version,
author = manifest.author, author = manifest.author,
gameVersion = manifest.minecraft.gameVersion, gameVersion = manifest.minecraft.gameVersion,
description = readTextFromZipFileQuietly(f, "modlist.html") ?: "No description", description = f.readTextZipEntryQuietly("modlist.html") ?: "No description",
manifest = manifest) manifest = manifest)
} }
@@ -147,7 +147,7 @@ class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDepende
} }
override fun execute() { override fun execute() {
unzipSubDirectory(zipFile, run, manifest.overrides) zipFile.uncompressTo(run, subDirectory = manifest.overrides)
var finished = 0 var finished = 0
for (f in manifest.files) { for (f in manifest.files) {

View File

@@ -42,7 +42,7 @@ class InstancePatch @JvmOverloads constructor(
val libraries: List<Library> = emptyList() val libraries: List<Library> = emptyList()
) )
class InstanceConfiguration(contentStream: InputStream) { class InstanceConfiguration(defaultName: String, contentStream: InputStream) {
/** /**
* The instance's name * The instance's name
*/ */
@@ -183,15 +183,17 @@ class InstanceConfiguration(contentStream: InputStream) {
showConsole = p.getProperty("ShowConsole") == "true" showConsole = p.getProperty("ShowConsole") == "true"
showConsoleOnError = p.getProperty("ShowConsoleOnError") == "true" showConsoleOnError = p.getProperty("ShowConsoleOnError") == "true"
wrapperCommand = p.getProperty("WrapperCommand") wrapperCommand = p.getProperty("WrapperCommand")
name = p.getProperty("name") name = defaultName
notes = p.getProperty("notes") notes = p.getProperty("notes") ?: ""
} }
} }
fun readMMCModpackManifest(f: File): Modpack { fun readMMCModpackManifest(f: File): Modpack {
ZipFile(f).use { zipFile -> ZipFile(f).use { zipFile ->
val entry = zipFile.getEntry("instance.cfg") ?: throw IOException("`instance.cfg` not found, $f is not a valid MultiMC modpack.") val firstEntry = zipFile.entries.nextElement()
val cfg = InstanceConfiguration(zipFile.getInputStream(entry)) val name = firstEntry.name.substringBefore("/")
val entry = zipFile.getEntry("$name/instance.cfg") ?: throw IOException("`instance.cfg` not found, $f is not a valid MultiMC modpack.")
val cfg = InstanceConfiguration(name, zipFile.getInputStream(entry))
return Modpack( return Modpack(
name = cfg.name, name = cfg.name,
version = "", version = "",
@@ -217,12 +219,12 @@ class MMCModpackInstallTask(private val dependencyManager: DefaultDependencyMana
override fun execute() { override fun execute() {
var version = repository.readVersionJson(name)!! var version = repository.readVersionJson(name)!!
unzipSubDirectory(zipFile, run, "minecraft/", false) zipFile.uncompressTo(run, subDirectory = "${manifest.name}/minecraft/", ignoreExistentFile = false, allowStoredEntriesWithDataDescriptor = true)
ZipFile(zipFile).use { zip -> ZipFile(zipFile).use { zip ->
for (entry in zip.entries) { for (entry in zip.entries) {
// ensure that this entry is in folder 'patches' and is a json file. // ensure that this entry is in folder 'patches' and is a json file.
if (!entry.isDirectory && entry.name.startsWith("patches/") && entry.name.endsWith(".json")) { if (!entry.isDirectory && entry.name.startsWith("${manifest.name}/patches/") && entry.name.endsWith(".json")) {
val patch = GSON.fromJson<InstancePatch>(zip.getInputStream(entry).readFullyAsString())!! val patch = GSON.fromJson<InstancePatch>(zip.getInputStream(entry).readFullyAsString())!!
val args = StringBuilder(version.minecraftArguments) val args = StringBuilder(version.minecraftArguments)
for (arg in patch.tweakers) for (arg in patch.tweakers)

View File

@@ -34,21 +34,22 @@ import java.io.IOException
*/ */
@JvmOverloads @JvmOverloads
@Throws(IOException::class) @Throws(IOException::class)
fun zip(src: File, destZip: File, pathNameCallback: ((String, Boolean) -> String?)? = null) { fun File.zipTo(destZip: File, pathNameCallback: ((String, Boolean) -> String?)? = null) {
ZipArchiveOutputStream(destZip.outputStream()).use { zos -> ZipArchiveOutputStream(destZip.outputStream()).use { zos ->
val basePath: String val basePath: String
if (src.isDirectory) if (this.isDirectory)
basePath = src.path basePath = this.path
else else
//直接压缩单个文件时,取父目录 //直接压缩单个文件时,取父目录
basePath = src.parent basePath = this.parent
zipFile(src, basePath, zos, pathNameCallback) zipFile(this, basePath, zos, pathNameCallback)
zos.closeArchiveEntry() zos.closeArchiveEntry()
} }
} }
/** /**
* Zip file. * Zip file.
*
* @param src source directory to be compressed. * @param src source directory to be compressed.
* @param basePath the file directory to be compressed, if [src] is a file, this is the parent directory of [src] * @param basePath the file directory to be compressed, if [src] is a file, this is the parent directory of [src]
* @param zos the [ZipOutputStream] of dest zip file. * @param zos the [ZipOutputStream] of dest zip file.
@@ -92,69 +93,21 @@ private fun zipFile(src: File,
/** /**
* Decompress the given zip file to a directory. * Decompress the given zip file to a directory.
* @param zip the source zip file. *
* @param dest the dest directory.
* @param callback will be called for every entry in the zip file, returns false if you dont want this file unzipped.
* @throws IOException
*/
@JvmOverloads
@Throws(IOException::class)
fun unzip(zip: File, dest: File, callback: ((String) -> Boolean)? = null, ignoreExistsFile: Boolean = true) {
val buf = ByteArray(1024)
dest.mkdirs()
ZipArchiveInputStream(zip.inputStream()).use { zipFile ->
if (zip.exists()) {
val strPath = dest.absolutePath
while (true) {
val zipEnt = zipFile.nextEntry ?: break
var strtemp: String
var gbkPath = zipEnt.name
if (callback != null)
if (!callback.invoke(gbkPath))
continue
if (zipEnt.isDirectory) {
strtemp = strPath + File.separator + gbkPath
val dir = File(strtemp)
dir.mkdirs()
} else {
//读写文件
gbkPath = zipEnt.name
strtemp = strPath + File.separator + gbkPath
//建目录
val strsubdir = gbkPath
for (i in 0 until strsubdir.length)
if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) {
val temp = strPath + File.separator + strsubdir.substring(0, i)
val subdir = File(temp)
if (!subdir.exists())
subdir.mkdir()
}
if (ignoreExistsFile && File(strtemp).exists())
continue
File(strtemp).outputStream().use({ fos ->
zipFile.copyTo(fos, buf)
})
}
}
}
}
}
/**
* Decompress the subdirectory of given zip file.
* @param zip the source zip file.
* @param dest the dest directory. * @param dest the dest directory.
* @param subDirectory the subdirectory of the zip file to be decompressed. * @param subDirectory the subdirectory of the zip file to be decompressed.
* @param callback will be called for every entry in the zip file, returns false if you dont want this file being uncompressed.
* @param ignoreExistentFile true if skip all existent files. * @param ignoreExistentFile true if skip all existent files.
* @param allowStoredEntriesWithDataDescriptor whether the zip stream will try to read STORED entries that use a data descriptor
* @throws IOException * @throws IOException
*/ */
@JvmOverloads @JvmOverloads
@Throws(IOException::class) @Throws(IOException::class)
fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExistentFile: Boolean = true) { fun File.uncompressTo(dest: File, subDirectory: String = "", callback: ((String) -> Boolean)? = null, ignoreExistentFile: Boolean = true, allowStoredEntriesWithDataDescriptor: Boolean = false) {
val buf = ByteArray(1024) val buf = ByteArray(1024)
dest.mkdirs() dest.mkdirs()
ZipArchiveInputStream(zip.inputStream()).use { zipFile -> ZipArchiveInputStream(this.inputStream(), null, true, allowStoredEntriesWithDataDescriptor).use { zipFile ->
if (zip.exists()) { if (this.exists()) {
val strPath = dest.absolutePath val strPath = dest.absolutePath
while (true) { while (true) {
val zipEnt = zipFile.nextEntry ?: break val zipEnt = zipFile.nextEntry ?: break
@@ -166,13 +119,18 @@ fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExisten
gbkPath = gbkPath.substring(subDirectory.length) gbkPath = gbkPath.substring(subDirectory.length)
if (gbkPath.startsWith("/") || gbkPath.startsWith("\\")) gbkPath = gbkPath.substring(1) if (gbkPath.startsWith("/") || gbkPath.startsWith("\\")) gbkPath = gbkPath.substring(1)
strtemp = strPath + File.separator + gbkPath strtemp = strPath + File.separator + gbkPath
if (callback != null)
if (!callback.invoke(gbkPath))
continue
if (zipEnt.isDirectory) { if (zipEnt.isDirectory) {
val dir = File(strtemp) val dir = File(strtemp)
dir.mkdirs() dir.mkdirs()
} else { } else {
//建目录 //建目录
val strsubdir = gbkPath val strsubdir = gbkPath
for (i in 0..strsubdir.length - 1) for (i in 0 until strsubdir.length)
if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) { if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) {
val temp = strPath + File.separator + strsubdir.substring(0, i) val temp = strPath + File.separator + strsubdir.substring(0, i)
val subdir = File(temp) val subdir = File(temp)
@@ -194,14 +152,14 @@ fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExisten
* Read the text content of a file in zip. * Read the text content of a file in zip.
* *
* @param f the zip file * @param f the zip file
* @param location the location of the text in zip file, something like A/B/C/D.txt * @param name the location of the text in zip file, something like A/B/C/D.txt
* @throws IOException if the file is not a valid zip file. * @throws IOException if the file is not a valid zip file.
* @return the content of given file. * @return the content of given file.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun readTextFromZipFile(f: File, location: String): String { fun File.readTextZipEntry(name: String): String {
ZipFile(f).use { zipFile -> ZipFile(this).use { zipFile ->
val entry = zipFile.getEntry(location) ?: throw IOException("`$location` not found.") val entry = zipFile.getEntry(name) ?: throw IOException("`$name` not found.")
return zipFile.getInputStream(entry).readFullyAsString() return zipFile.getInputStream(entry).readFullyAsString()
} }
} }
@@ -213,10 +171,9 @@ fun readTextFromZipFile(f: File, location: String): String {
* @param location the location of the text in zip file, something like A/B/C/D.txt * @param location the location of the text in zip file, something like A/B/C/D.txt
* @return the content of given file. * @return the content of given file.
*/ */
fun readTextFromZipFileQuietly(f: File, location: String): String? { fun File.readTextZipEntryQuietly(location: String) =
try { try {
return readTextFromZipFile(f, location) readTextZipEntry(location)
} catch (e: IOException) { } catch (e: IOException) {
return null null
} }
}