Source:
In this tutorial, I will guide you how to archive/zip files or directory using standard Java™ class library in the package of java.util.zip.*. After we grasp it, we'll move on to the lesson about sending it as an attachment in an email over the network using Gmail SMTP. All the classes we need to create the email application are available in the javax.mail package.
All we need to archive a file is:
if(Files.isRegularFile(source)) {
ZipEntry entry = new ZipEntry(source.toString());
try(FileInputStream in = new FileInputStream(source.toFile());
ZipOutputStream zOut = new ZipOutputStream(new FileOutputStream(target.toFile()))) {
zOut.putNextEntry(entry);
/*
* Hold 2MB in memory.
*/
byte[] BYTES = new byte[2048];
for(int length; (length = in.read(BYTES)) > -1;)
zOut.write(BYTES, 0, length);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
Archive a directory is a bit more complicated, because we will recursively match the representation of a directory in which there are files and other directories inside it.
Here's the codes:
public static void test(Path source) {
if(Files.isDirectory(source)) {
List<Path> paths = new java.util.ArrayList<>();
try(Stream<Path> stream = Files.walk(source, 1)) {
stream.forEach((path) -> paths.add(path));
} catch(Exception e) {
throw new RuntimeException(e);
paths.clear();
return;
}
paths.remove(source);
if(paths.size() > 0)
for(Path e : paths)
//Recursively, the same action as in archiving files as above
zip(e);
else {
//Keep zipping an empty directory
String name = source.toString();
try { zOut.putNextEntry(new ZipEntry(name + java.io.File.separator)); }
catch(Exception e) {
throw new RuntimeException(e);
}
}
}
}
By setting file archiving as the base case, we can now define the zip method based on a path completely as follows:
Right click on src/main/java directory. And on pop-up dialog, type:
com.murez.branch.io.ZipIOStream.
Next, type this source code:
package com.murez.branch.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static java.io.File.separator;
public class ZipIOStream {
/**
* Zip the files or directory.
* @param source The path to be zipped
* @param target The path of file as the output of zipping {@code source}
* @param report Map of errors that occurred while zipping {@code source}. Or just pass {@code null}, if you want to throw the exception.
* @return {@code True} if this process was successful, otherwise {@code false}.
*/
public static boolean zip(Path source, Path target, Map report) {
ZipIOStream zipStream;
boolean b = report == null;
try{ zipStream = new ZipIOStream(source, target); }
catch(Throwable e) {
e = new RuntimeException("Initializing was failed", e);
if(b) throw (RuntimeException) e;
report.put(null, e);
return false;
}
if(!zipStream.tryLock(b))
return false;
zipStream.zip(source, report);
/*
* Make sure all sources are closed by first executing the `close()` method under the conditions below.
*/
return zipStream.close(b) && (b || report.size() < 1);
}
private final FileOutputStream F_OUT;
private final ZipOutputStream Z_OUT;
private final int N;
private FileLock lock;
private ZipIOStream(Path source, Path target) throws Throwable {
if(source == null || Files.notExists(source))
throw new IllegalArgumentException("The source never existed");
if(target == null)
throw new IllegalArgumentException("The target can't be null");
F_OUT = new FileOutputStream(target.toFile());
Z_OUT = new ZipOutputStream(F_OUT);
N = source.getNameCount() - 1;
}
private void zip(Path source, Map report) {
final byte[] BYTES;
if(Files.isRegularFile(source)) {
ZipEntry entry = new ZipEntry(source.subpath(N, source.getNameCount()).toString());
try(FileInputStream in = new FileInputStream(source.toFile())) {
Z_OUT.putNextEntry(entry);
/*
* Hold 2MB in memory.
*/
BYTES = new byte[2048];
for(int length; (length = in.read(BYTES)) > -1;)
Z_OUT.write(BYTES, 0, length);
} catch(Exception e) {
e = new RuntimeException(source.toString(), e);
if(report == null) throw (RuntimeException) e;
report.put(source, e);
}
} else
if(Files.isDirectory(source)) {
List paths = new java.util.ArrayList();
try(Stream stream = Files.walk(source, 1)) {
stream.forEach(paths::add);
} catch(Exception e) {
e = new RuntimeException("Walking path was failed: " + source, e);
if(report == null) throw (RuntimeException) e;
report.put(source, e);
paths.clear();
return;
}
paths.remove(source);
if(paths.size() > 0)
for(Path e : paths) zip(e, report);
else {
String name = source.subpath(N, source.getNameCount()).toString();
try { Z_OUT.putNextEntry(new ZipEntry(name + separator)); }
catch(Exception e) {
e = new RuntimeException(source.toString(), e);
if(report == null) throw (RuntimeException) e;
report.put(source, e);
}
}
} else {
if(report != null)
report.put(source, new IllegalArgumentException("Source's neither a file nor a directory"));
}
}
private boolean close(boolean thrown) {
boolean b = false;
try {
if(lock != null)
lock.close();
Z_OUT.close();
b = true;
} catch(IOException e) {
if(thrown) throw new RuntimeException("Closing resources was failed", e);
}
return b;
}
private boolean tryLock(boolean thrown) {
try { lock = F_OUT.getChannel().tryLock(); }
catch(Throwable e) {
if(thrown) throw new RuntimeException("Locking the target path has failed", e);
return false;
}
return true;
}
}
Here's a test that we will do:
Main
public static void main(String[] args) {
final String ROOT = "D:/";
Path source = Paths.get(ROOT, "Sandbox/Test/Test");
Path target = Paths.get(ROOT, "Sandbox/Test/Out.zip");
Map report = new java.util.HashMap();
if(!zip(source, target, report)) {
System.out.println("Failed!");
for(Map.Entry e : report.entrySet()) {
System.out.println("Path: " + e.getKey());
e.getValue().printStackTrace();
}
}
else
System.out.println("Success");
}
Input: D:/Sandbox/Test/Test
Log
Output: D:/Sandbox/Test/Out.zip
Congratulations! We have successfully completed the module to create a ZIP file using the Java™ standard classes.
All we need to send an email with attachment is:
build.gradle to be able to use the classes in the javax.mail package.compile group: 'javax.mail', name: 'mail', version: '1.4.1'Allow less secure apps: ON.https://myaccount.google.com/lesssecureapps
private static final java.util.Properties GMAIL = new java.util.Properties();
static {
GMAIL.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
GMAIL.put("mail.smtp.socketFactory.port", "465");
GMAIL.put("mail.smtp.ssl.enable", "true");
GMAIL.put("mail.smtp.auth", "true");
GMAIL.put("mail.smtp.host", "smtp.gmail.com");
GMAIL.put("mail.smtp.port", "465");
}
And here's the complete code:
package com.murez.branch.io;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.nio.file.Path;
public class Mail {
private final Message MESSAGE;
private static final java.util.Properties GMAIL = new java.util.Properties();
static {
GMAIL.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
GMAIL.put("mail.smtp.socketFactory.port", "465");
GMAIL.put("mail.smtp.ssl.enable", "true");
GMAIL.put("mail.smtp.auth", "true");
GMAIL.put("mail.smtp.host", "smtp.gmail.com");
GMAIL.put("mail.smtp.port", "465");
}
public static Mail getInstance(String username, String password, String receiver, boolean thrown) {
try {
return new Mail(username, password, receiver);
} catch(Throwable e) {
if(thrown)
throw new RuntimeException(e);
return null;
}
}
public static Mail getInstance(String username, String password, String receiver) {
return getInstance(username, password, receiver, false);
}
private Mail(String username, String password, String receiver) throws Throwable {
Session session = Session.getInstance(GMAIL, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
MESSAGE = new MimeMessage(session);
MESSAGE.setFrom(new InternetAddress(username));
MESSAGE.setRecipient(Message.RecipientType.TO, new InternetAddress(receiver));
}
public void setContent(String message, boolean thrown, Path... files) {
Multipart multiPart = new MimeMultipart();
BodyPart body;
try {
(body = new MimeBodyPart()).setText(message);
multiPart.addBodyPart(body);
} catch(MessagingException e) {
if(thrown)
throw new RuntimeException(e);
}
if(files != null)
for(Path file : files)
try {
body = new MimeBodyPart();
body.setDataHandler(new DataHandler(new FileDataSource(file.toFile())));
body.setFileName(file.getFileName().toString());
multiPart.addBodyPart(body);
} catch(MessagingException e) {
if(thrown)
throw new RuntimeException(file + " > " + e.getMessage(), e);
}
try { MESSAGE.setContent(multiPart); }
catch(MessagingException e) {
if(thrown)
throw new RuntimeException(e);
}
}
public boolean setSubject(String subject, boolean thrown) {
boolean b = true;
try { MESSAGE.setSubject(subject); }
catch(MessagingException e) {
if(thrown)
throw new RuntimeException(e);
b = false;
}
return b;
}
public boolean send(boolean thrown) {
boolean b = true;
try { Transport.send(MESSAGE); }
catch(MessagingException e) {
if(thrown)
throw new RuntimeException(e);
b = false;
}
return b;
}
}
Test
Suppose the two google accounts are [email protected] as the sender and [email protected] as the recipient, and the following is the scenario:
Main
public static void main(String[] args) {
String sender = "rezanas***@gmail.com";
String password = "**************";
String receiver = "murez***@gmail.com";
Mail mail = Mail.getInstance(sender, password, receiver, true);
if(mail == null) {
System.out.println("Failed!");
return;
}
if(!mail.setSubject("Test")) {
System.out.println("Submission of subject has failed");
return;
}
/*
* Plain text file
*/
final String ROOT = "D:\\Sandbox\\Test";
Path plainText = Paths.get(ROOT, "Plain.txt");
/*
* Archive file
*/
Path zipFile = Paths.get(ROOT, "Out.zip");
ZipIOStream.zip(Paths.get(ROOT, "Test"), zipFile, null);
System.out.println("Zipping was complete!");
mail.setContent("Hello World! My First email using Java mail", true, plainText, zipFile);
if(!mail.send(true)) {
System.out.println("Sending has failed");
return;
}
System.out.println("Successful!");
}
Input
D:\Sandbox\Test\Plain.txt and D:\Sandbox\Test\Out.zipLog
Sender
Recipient
Congratulations! We have successfully sent an email using Gmail SMTP and have finished this tutorial completely.
Share with Heart.