使用Java在Linux中使用7-zip压缩一个文件夹。

huangapple go评论74阅读模式
英文:

Compress a folder using 7-zip in Linux with Java

问题

Here's the translated code portion:

我在我的代码中有以下函数

public static void runExecutable(String filePath, String command, boolean commandLine, boolean wait) {
		
    InputStream inputStream = CommonMethods.class.getClassLoader().getResourceAsStream(filePath);
    String tempFileSuffix = "";
    switch (CommonMethods.getOperatingSystem()) {
        case "Windows":
            tempFileSuffix = ".exe";
            break;
        case "Linux":
        case "Mac":
            tempFileSuffix = ".bin";
            break;
    }
    
    File tempFile;
    try {
        tempFile = File.createTempFile("AutoFPCurator", tempFileSuffix);
    } catch (IOException e) {
        new ErrorDialog(e);
        return;
    }
    
    try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }
        outputStream.close();
    } catch (IOException e) {
        new ErrorDialog(e);
        return;
    }
    
    String cmdProgram = "";
    String cFlag = "-c";
    switch (CommonMethods.getOperatingSystem()) {
        case "Windows": 
            cmdProgram = "cmd.exe";
            cFlag = "/c";
            break;
        case "Mac":
            cmdProgram = "sh";
            break;
        case "Linux":
            cmdProgram = "bash";
            break;
        default:
            return;
    }
    
    ProcessBuilder processBuilder = !commandLine ? new ProcessBuilder(tempFile.getAbsolutePath(), command)
                            : new ProcessBuilder(cmdProgram, cFlag, tempFile.getAbsolutePath(), command);
    processBuilder.redirectErrorStream(true);
    
    Process process;
    try {
        process = processBuilder.start();
    } catch (IOException e) {
        new ErrorDialog(e);
        return;
    }
    if (!wait) {
        return;
    }
    
    try {
        process.waitFor();
    } catch (InterruptedException e) {
        new ErrorDialog(e);
    }
}

If you need further assistance with troubleshooting the Linux issue, please feel free to ask.

英文:

I have the following function in my code.

public static void runExecutable(String filePath, String command, boolean commandLine, boolean wait) {
InputStream inputStream = CommonMethods.class.getClassLoader().getResourceAsStream(filePath);
String tempFileSuffix = "";
switch (CommonMethods.getOperatingSystem()) {
case "Windows":
tempFileSuffix = ".exe";
break;
case "Linux":
case "Mac":
tempFileSuffix = ".bin";
break;
}
File tempFile;
try {
tempFile = File.createTempFile("AutoFPCurator", tempFileSuffix);
} catch (IOException e) {
new ErrorDialog(e);
return;
}
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
} catch (IOException e) {
new ErrorDialog(e);
return;
}
String cmdProgram = "";
String cFlag = "-c";
switch (CommonMethods.getOperatingSystem()) {
case "Windows": 
cmdProgram = "cmd.exe";
cFlag = "/c";
break;
case "Mac":
cmdProgram = "sh";
break;
case "Linux":
cmdProgram = "bash";
break;
default:
return;
}
ProcessBuilder processBuilder = !commandLine ? new ProcessBuilder(tempFile.getAbsolutePath(), command)
: new ProcessBuilder(cmdProgram, cFlag, tempFile.getAbsolutePath(), command);
processBuilder.redirectErrorStream(true);
Process process;
try {
process = processBuilder.start();
} catch (IOException e) {
new ErrorDialog(e);
return;
}
if (!wait) {
return;
}
try {
process.waitFor();
} catch (InterruptedException e) {
new ErrorDialog(e);
}
}

This code is designed to run executables from src/main/resources/, as it specifies, however it's especially used to compress folders into 7z files (using standalone 7z executables in its src/main/resources directory. I use executables because 7Z compression algorithms made for Java (such as Apache Commons's), doesn't compress it the way I need it to be compressed for my purposes, unlike the executable. I downloaded the Linux 7z executable, extracted it, and used 7zz in my program). When I call it like so:

switch (CommonMethods.getOperatingSystem()) {
case "Windows":
exeString = "programs/7zip/7za.exe";
break;
case "Mac":
exeString = "programs/7zip/7zzmac";
break;
case "Linux":
exeString = "programs/7zip/7zznux";
break;
default:
System.out.println(commonStrs.get("unsupportedOS"));
return;
}
CommonMethods.runExecutable(exeString, args, true, true);

It works on Windows, but I tried it on Linux and it didn't work. I've been trying to pinpoint the problem. The arguments are valid and it does appear to be running, but it just doesn't compress the folder. I ran the raw argument that the function builds myself into Linux Terminal and it said "Permission denied" in response to my attempt to access to the tmp folder (which is where the temporary 7zip executable is stored). I tried storing it in an alternate location that is perfectly accessible to no avail.

TL;DR, how do I get this working in Linux (and Mac by extension maybe?)? Just to be clear, this works on Windows.

答案1

得分: 1

在macOS(Ventura 13.4)上,我下载了7zz的可执行文件。然后,我编写了一个快速程序来验证我是否可以通过ProcessBuilder直接运行可执行文件(无需使用sh)。这一步成功了。

然后,我将7zz添加到我的应用程序资源中,并尝试将其提取到临时文件。这一步失败了,因为该文件不可执行(File#canExecutefalse)。

接着,根据https://stackoverflow.com/questions/664432/how-do-i-programmatically-change-file-permissions,我修改了临时文件的执行属性(在提取后),这一步成功了。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) {
        try {
            new Main();
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public Main() throws IOException, InterruptedException {
        File binary = extractBinary();
        System.out.println(binary);
        System.out.println("Executable: " + binary.canExecute());
        // This could be moved to extractBinary
        // But I wanted to demonstrate the difference between
        // the two binary.canExecute() statements
        Files.setPosixFilePermissions(binary.toPath(), PosixFilePermissions.fromString("rwxr-x---"));
        System.out.println("Executable: " + binary.canExecute());

        ProcessBuilder pb = new ProcessBuilder(
                binary.getAbsolutePath(), 
                "a", 
                "/Users/shane.whitehead/Downloads/Test.7z", 
                "/Users/shane.whitehead/Downloads/7z2300-mac.tar/*"
        );
        pb.redirectErrorStream(true);
        Process p = pb.start();
        InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
        consumer.start();

        p.waitFor();

        consumer.join();
        System.out.println(consumer.getOutput());
    }

    protected File extractBinary() throws IOException {
        File tempFile = File.createTempFile("7zBinary", ".bin");
        try (FileOutputStream fos = new FileOutputStream(tempFile); InputStream is = getClass().getResourceAsStream("/bin/7zz")) {
            is.transferTo(fos);
        }
        return tempFile;
    }

    // This is just here to make my life easier
    public class InputStreamConsumer extends Thread {

        private InputStream is;
        private IOException exp;
        private StringBuilder output;

        public InputStreamConsumer(InputStream is) {
            this.is = is;
        }

        @Override
        public void run() {
            int in = -1;
            output = new StringBuilder(64);
            try {
                while ((in = is.read()) != -1) {
                    output.append((char) in);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
                exp = ex;
            }
        }

        public StringBuilder getOutput() {
            return output;
        }

        public IOException getException() {
            return exp;
        }
    }
}

你可能会发现,首先在每个平台上找到一个可行的解决方案,然后根据这些要求设计你的解决方案会更容易,也许可以创建一个基于平台生成ProcessBuilder的工厂作为一个思路。

英文:

On macOS (Ventura 13.4), I downloaded the executable for 7zz. I then wrote a quick program to verify that I could run the executable directly via ProcessBuilder (no need for a sh). This was successful.

I then added 7zz to my applications resources and proceeded to extract it to a temp file. This failed as the file was not executable (File#canExecute was false).

Then, based on https://stackoverflow.com/questions/664432/how-do-i-programmatically-change-file-permissions, I modified the execution attribute of the temp file (after it was extracted) and this was successful

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
public static void main(String[] args) {
try {
new Main();
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
public Main() throws IOException, InterruptedException {
File binary = extractBinary();
System.out.println(binary);
System.out.println("Executable: " + binary.canExecute());
// This could be moved to extractBinary
// But I wanted to demonstrate the difference between
// the two binary.canExecute() statements
Files.setPosixFilePermissions(binary.toPath(), PosixFilePermissions.fromString("rwxr-x---"));
System.out.println("Executable: " + binary.canExecute());
ProcessBuilder pb = new ProcessBuilder(
binary.getAbsolutePath(), 
"a", 
"/Users/shane.whitehead/Downloads/Test.7z", 
"/Users/shane.whitehead/Downloads/7z2300-mac.tar/*"
);
pb.redirectErrorStream(true);
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
}
protected File extractBinary() throws IOException {
File tempFile = File.createTempFile("7zBinary", ".bin");
try (FileOutputStream fos = new FileOutputStream(tempFile); InputStream is = getClass().getResourceAsStream("/bin/7zz")) {
is.transferTo(fos);
}
return tempFile;
}
// This is just here to make my life easier
public class InputStreamConsumer extends Thread {
private InputStream is;
private IOException exp;
private StringBuilder output;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
@Override
public void run() {
int in = -1;
output = new StringBuilder(64);
try {
while ((in = is.read()) != -1) {
output.append((char) in);
}
} catch (IOException ex) {
ex.printStackTrace();
exp = ex;
}
}
public StringBuilder getOutput() {
return output;
}
public IOException getException() {
return exp;
}
}
}

You might find it easier to first find a working solution on each platform and then design your solution around those requirements, maybe having a factory which generated the ProcessBuilder based on the platform, as an idea.

huangapple
  • 本文由 发表于 2023年5月26日 07:37:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76336823.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定