Lesson 16 - Working with custom files in Java - Zip archive
In the previous lesson, Working with files and folders in Java - New API, we showed how to work with files and folders in Java. Now you can work with many kinds of files, you can save and open them, play them, catch errors, ... But imagine a real application to manage employees. An employee will have text data (first name, last name, and email), a date (date of birth), a number (phone number) and a picture (their photo). Just for the sake of the picture it could be a problem to save all this data in one single file. Theoretically, we could choose some binary data format (because of the image) and insert text data into it. But it practice, binary files are quite awkward and it's hard to keep up with format changes.
Zip
Similar applications often use archives to store data. Consider MS-Word and
its documents with the .docx
file extension. If you change the
extension of any .docx
file to .zip
, you will find
that the document is actually a zip archive, with only a different extension.
Try it.
Windows hide file extensions of known file types by default, so
you can see Document
instead of Document.docx
. To
change the settings, go to Control Panel -> Switch to the Icons view ->
Folder Options -> Uncheck Hide extensions of known types.
Archives can contain multiple files and the user sees them as one from the
outside without suspecting anything like that. The zip archive is exactly what
we'll use to store our employee. And instead of .zip
we'll set the
file a completely different extension, it can be .employee
or if
you want to stick to three letters, just .emp
.
The only thing the file extension does is telling Windows what application it
should use to open the file when the user double-clicks it. Usually, each file
name ends with a dot and a 3-letter extension. In fact, the filename doesn't
have to have an extension at all, may have more of them, it may be longer than 3
characters, and may not even match what is really stored in the file. We'll mask
the .zip
file as .emp
. We won't keep the original
.zip
extension to prevent the confused user from unpacking it and
modifying the files.
The .emp file format
The zip folder structure could be as follows:
data.emp
employees.xml
images/
firstnamelastname_1.png
firstnamelastname_2.png
The employees.xml
file might look like this:
<employees> <employee> <firstName>Thomas</firstName> <lastName>Cooper</lastName> <email>[email protected]</email> <phone>123456789</phone> <birthdate>1/1/1970</birthdate> </employee> </employees>
The <employees>
element wrapping the
employees is here so the application can handle multiple employees in the
future. It's necessary to think about such "small things" in the design
phase.
Creating the Employee application
Create a new project, form application. An archive with the project is available below the article. There are all necessary form elements already generated in the project and the buttons have handling methods prepared.
Class Employee
Let's start with what should be clear - properties. Our employee will have:
- first name,
- last name,
- email,
- telephone number,
- birthdate,
- and will have a photo:
public class Employee { private String firstName; private String lastName; private String email; private String phone; private LocalDate birthdate; private BufferedImage image; public Employee() { this("", "", "", "", LocalDate.now()); } public Employee(String firstName, String lastName, String email, String phone, LocalDate birthdate) { this.firstName = firstName; this.lastName = lastName; this.email = email; this.phone = phone; this.birthdate = birthdate; } @Override public String toString() { return "Employee{" + "first name=" + firstName + ", last name=" + lastName + '}'; } }
The class has two constructors. The first overload creates an empty employee. We use the second constructor to create an employee instance with all the data except for the image.
Getters and setters
The following code are just getters and setters for the class properties:
public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public LocalDate getBirthdate() { return birthdate; } public void setBirthdate(LocalDate birthdate) { this.birthdate = birthdate; } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this.image = image; }
We'll add an Employee
variable to the form, but we won't
instantiate it:
private Employee employee;
Employee manager
To keep the logic and GUI layers separated, we'll create a new class to
manage employees. We'll name the class simply EmployeeManager
. The
class will contain a collection of employees. The collection will be
DefaultListModel
so we can use it for more employees later.
private final DefaultListModel<Employee> employees = new DefaultListModel<>();
Next, we'll create four methods in the class: save()
,
load()
, getFirst()
, and saveFirst()
.
We'll implement the save()
and load()
methods in the
next lesson. The last two methods are here just to keep things simple, so that
we can work with one employee easily. Their implementation will be as
follows:
public Employee getFirst() { return employees.size() == 0 ? new Employee() : employees.get(0); } public void saveFirst(Employee employee) { this.employees.clear(); this.employees.addElement(employee); }
Editing the form
That would be everything in the manager for now. Let's move to the form for a
second and create there a manager instance. We'll let it instantiate the
Employee
class for us:
private final EmployeeManager manager = new EmployeeManager(); private Employee employee = manager.getFirst();
In addition, we'll add two helper methods to the form's code to update the
information on the form and in the Employee
instance:
private void refreshForm() { jTxtFirstName.setText(employee.getFirstName()); jTxtLastName.setText(employee.getLastName()); jTxtEmail.setText(employee.getEmail()); jTxtPhoneNumber.setText(employee.getPhone()); jTxtBirthday.setText(Constants.FORMAT_DATA.format(employee.getBirthdate())); jPanel1.getGraphics().drawImage(employee.getImage(), 0, 0, 123, 135, null); } private void loadFromForm() { employee.setFirstName(jTxtFirstName.getText()); employee.setLastName(jTxtLastName.getText()); employee.setEmail(jTxtEmail.getText()); employee.setPhone(jTxtPhoneNumber.getText()); employee.setBirthdate(LocalDate.parse(jTxtBirthday.getText(), Constants.FORMAT_DATA)); }
Button handling
Finally, let's look at the button handlers.
Selecting the image
The first handler we're going to implement is the image selection button:
private void jBtnImageActionPerformed(java.awt.event.ActionEvent evt) { loadFromForm(); JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(new FileNameExtensionFilter("Images...", "png")); final int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { final File image = fileChooser.getSelectedFile(); try { employee.setImage(ImageIO.read(image)); refreshForm(); } catch (IOException e) { e.printStackTrace(); } } }
First, we save any values from the form components to the
Employee
class instance. Next, we create a new
JFileChooser
dialog to select the user image. If the image is
selected successfully, ImageIO.read()
will load the image and save
it to the employee. Finally, we call the refreshForm()
method to
display the image on the form.
Saving
The handler of the save button will look like this:
private void jBtnSaveActionPerformed(java.awt.event.ActionEvent evt) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(new FileNameExtensionFilter("Employee files...", "emp")); final int result = fileChooser.showSaveDialog(this); if (result == JFileChooser.APPROVE_OPTION) { final File employeeFile = fileChooser.getSelectedFile(); try { loadFromForm(); manager.saveFirst(employee); manager.save(employeeFile); } catch (IOException | XMLStreamException e) { e.printStackTrace(); } } }
Using JFileChooser
dialog, we select the file to which we want
to store our data. If we select the file, we load the data from the form into
the employee instance. We pass this instance to the manager to save the data.
Finally, using the save()
method we write all the data to the
file.
Loading
As the last thing, we'll show how to load the data back:
private void jBtnLoadActionPerformed(java.awt.event.ActionEvent evt) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(new FileNameExtensionFilter("Employee files...", "emp")); final int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { final File employeeFile = fileChooser.getSelectedFile(); try { manager.load(employeeFile); employee = manager.getFirst(); refreshForm(); } catch (IOException | ParserConfigurationException | SAXException e) { e.printStackTrace(); } } }
Once again, we display the JFileChooser
dialog in the method,
this time to select the employee. If we select the file, we call the
load()
method on the EmployeeManager
class instance.
After loading the data successfully, we display the data of the first employee
on the form.
Now we have the application designed. Next time, in the lesson Working with custom files in Java - Saving and loading ZIPs, we'll finally look at the actual processing of ZIP files in Java and export and import our employees.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 18x (40.55 kB)
Application includes source codes in language Java