001/*
002 * The contents of this file are subject to the license and copyright detailed
003 * in the LICENSE and NOTICE files at the root of the source tree.
004 */
005package org.duraspace.bagit;
006
007import java.io.IOException;
008import java.io.InputStream;
009import java.io.OutputStream;
010import java.nio.file.Files;
011import java.nio.file.Path;
012import java.util.List;
013import java.util.stream.Collectors;
014
015import org.apache.commons.compress.archivers.ArchiveEntry;
016import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
017import org.apache.commons.compress.utils.IOUtils;
018
019/**
020 * Serialize a BagIt bag into a zip archive without compression
021 *
022 * @author mikejritter
023 * @since 2020-02-24
024 */
025public class ZipBagSerializer implements BagSerializer {
026    private final String extension = ".zip";
027
028    @Override
029    public Path serialize(final Path root) throws IOException {
030        final Path parent = root.getParent().toAbsolutePath();
031        final String bagName = root.getFileName().toString();
032
033        final Path serializedBag = parent.resolve(bagName + extension);
034        try(final OutputStream os = Files.newOutputStream(serializedBag);
035            final ZipArchiveOutputStream zip = new ZipArchiveOutputStream(os)) {
036
037            // it would be nice not to have to collect the files which are walked, but we're required to try/catch
038            // inside of a lambda which isn't the prettiest. maybe a result could be returned which contains either a
039            // Path or the Exception thrown... just an idea
040            final List<Path> files = Files.walk(root).collect(Collectors.toList());
041            for (Path bagEntry : files) {
042                final String name = parent.relativize(bagEntry).toString();
043                final ArchiveEntry entry = zip.createArchiveEntry(bagEntry.toFile(), name);
044                zip.putArchiveEntry(entry);
045                if (bagEntry.toFile().isFile()) {
046                    try (InputStream inputStream = Files.newInputStream(bagEntry)) {
047                        IOUtils.copy(inputStream, zip);
048                    }
049                }
050                zip.closeArchiveEntry();
051            }
052        }
053
054        return serializedBag;
055    }
056}