英文:
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#canExecute
为false
)。
接着,根据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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论