Witajcie!
Stworzyłem sobie małego konsolowego zippera z użyciem wielowątkowości, którą aktualnie poznaję.
Mam dwa problemy:
- W zipie zostaje tylko jeden plik - ostatni z kolejki
-
ExecutorService
(konkretnieSingleThreadExecutor
) nie zatrzymuje głównego wątku programu, przez co wiadomość o poprawnym utworzeniu zipa jest wyświetlana na początku
Klasa ZipFile
, która ma za zadanie dodać tylko jeden plik do zipa:
package com.burdzi0;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Observable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFile extends Observable{
private File file;
private File target;
private long status = 0;
private long fileLength;
/*
* `lastSentStatus` and `currentStatus` are indicators
* for the Observable's update methods. The file's length
* can be big enough to indicate the same value a few times.
* That makes the terminal/command line flicker.
* In order to prevent that situation the program checks
* if the value has changed and then decides to execute
* update method or not.
*/
private int lastSentStatus = 0;
private int currentStatus = 0;
public ZipFile(File file, File target) {
this.file = file;
this.target = target;
fileLength = file.length();
}
private ZipEntry createZipEntry() {
return new ZipEntry(file.getName());
}
public void zipIt() {
try (FileInputStream fileIn = new FileInputStream(file);
ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(target, true))) {
zipOut.putNextEntry(createZipEntry());
int len;
byte[] buffer = new byte[1024];
while ((len = fileIn.read(buffer)) != -1) {
zipOut.write(buffer);
status += len;
// Here is being calculated the percentage of
// the file's length already sent
currentStatus = (int) (status * 100.0 / fileLength);
// If the currentStatus value is the same as
// the lastSentStatus the update method
// won't be executed
if (currentStatus > lastSentStatus) {
setChanged();
notifyObservers(new Integer(currentStatus));
lastSentStatus = currentStatus;
}
}
System.out.println("\n[DONE] Zipped file " + file.getName() + " successfully!");
} catch (FileNotFoundException e) {
System.out.println("[ERROR] Couldn't find file " + file.getName());
} catch (IOException e) {
System.out.println("[ERROR] The I/O system is invalid");
}
}
}
Klasa ZipRunnable
, której zadaniem jest zaimplementowanie interfejsów Runnable
i Observer
.
package com.burdzi0;
import java.io.File;
import java.util.Observable;
import java.util.Observer;
public class ZipRunnable implements Runnable, Observer{
private File file;
private File target;
public ZipRunnable(File file, File target) {
this.file = file;
this.target = target;
}
@Override
public void run() {
ZipFile zipper = new ZipFile(file, target);
zipper.addObserver(this);
System.out.println("[INFO] Starting zipping file " + file.getName());
zipper.zipIt();
}
@Override
public void update(Observable o, Object arg) {
System.out.print("\r[STATUS] Zipping file " + file.getName() + ": " + ((Integer)(arg)).intValue() + " %");
}
}
Klasa Test
do testowania aplikacji.
package com.burdzi0;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
List<File> files = new ArrayList<File>();
files.add(new File("I:\\Muzyka\\Wolves At The Gate - Types & Shadows (2016)\\01. Asleep.mp3"));
files.add(new File("I:\\Muzyka\\Wolves At The Gate - Types & Shadows (2016)\\02. Flickering Flame.mp3"));
File target = new File("I:\\Programowanie\\ONE.zip");
ExecutorService executor = Executors.newSingleThreadExecutor();
for (File file: files) {
executor.execute(new ZipRunnable(file, target));
}
executor.shutdown();
System.out.println("[DONE] Successfully created " + target.getName() + "!");
}
}
Wszelkie pomysły mile widziane ;)