com.facebook.buck.step.fs.ZipDirectoryWithMaxDeflateStep.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.step.fs.ZipDirectoryWithMaxDeflateStep.java

Source

/*
 * Copyright 2012-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.facebook.buck.step.fs;

import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.io.Files;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Command to zip up a directory while respecting a file size limit to be deflated.
 * <p>
 * Currently, Froyo has a limitation where it will not deflate files out of a zip if the inflated
 * size exceeds 1 MiB (1 << 20 bytes).  This utility is useful for including assets into the APK
 * where we deflate those that we can, and store the ones we can't.
 */
public class ZipDirectoryWithMaxDeflateStep implements Step {

    /**
     * Extensions of files we don't want to DEFLATE (e.g. already compressed).
     */
    private final Set<String> EXTENSIONS_NOT_TO_DEFLATE = ImmutableSet.of("gzip", "jar", "jpg", "png", "xz");

    private final String inputDirectoryPath;
    private final String outputZipPath;
    private final long maxDeflatedBytes;

    public ZipDirectoryWithMaxDeflateStep(String inputDirectoryPath, String outputZipPath) {
        this(inputDirectoryPath, outputZipPath, 0);
    }

    public ZipDirectoryWithMaxDeflateStep(String inputDirectoryPath, String outputZipPath, long maxDeflatedBytes) {
        Preconditions.checkState(maxDeflatedBytes >= 0);

        this.maxDeflatedBytes = maxDeflatedBytes;
        this.inputDirectoryPath = Preconditions.checkNotNull(inputDirectoryPath);
        this.outputZipPath = Preconditions.checkNotNull(outputZipPath);
    }

    @Override
    public int execute(ExecutionContext context) {
        File inputDirectory = new File(inputDirectoryPath);
        Preconditions.checkState(inputDirectory.exists() && inputDirectory.isDirectory());

        Closer closer = Closer.create();
        try {
            ImmutableMap.Builder<File, ZipEntry> zipEntriesBuilder = ImmutableMap.builder();
            addDirectoryToZipEntryList(inputDirectory, "", zipEntriesBuilder);
            ImmutableMap<File, ZipEntry> zipEntries = zipEntriesBuilder.build();

            if (!zipEntries.isEmpty()) {
                ZipOutputStream outputStream = closer.register(
                        new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outputZipPath))));

                for (Map.Entry<File, ZipEntry> zipEntry : zipEntries.entrySet()) {
                    outputStream.putNextEntry(zipEntry.getValue());
                    ByteStreams.copy(Files.newInputStreamSupplier(zipEntry.getKey()), outputStream);
                    outputStream.closeEntry();
                }
            }
        } catch (IOException e) {
            e.printStackTrace(context.getStdErr());
            return 1;
        } finally {
            try {
                closer.close();
            } catch (IOException e) {
                Throwables.propagate(e);
            }
        }
        return 0;
    }

    private void addDirectoryToZipEntryList(File directory, String currentPath,
            ImmutableMap.Builder<File, ZipEntry> zipEntriesBuilder) throws IOException {
        Preconditions.checkNotNull(currentPath);

        for (File inputFile : directory.listFiles()) {
            String childPath = currentPath + (currentPath.isEmpty() ? "" : "/") + inputFile.getName();

            if (inputFile.isDirectory()) {
                addDirectoryToZipEntryList(inputFile, childPath, zipEntriesBuilder);
            } else {
                ZipEntry nextEntry = new ZipEntry(childPath);
                long fileLength = inputFile.length();
                if (fileLength > maxDeflatedBytes
                        || EXTENSIONS_NOT_TO_DEFLATE.contains(Files.getFileExtension(inputFile.getName()))) {
                    nextEntry.setMethod(ZipEntry.STORED);
                    nextEntry.setCompressedSize(inputFile.length());
                    nextEntry.setSize(inputFile.length());
                    HashCode crc = ByteStreams.hash(Files.newInputStreamSupplier(inputFile), Hashing.crc32());
                    nextEntry.setCrc(crc.padToLong());
                }

                zipEntriesBuilder.put(inputFile, nextEntry);
            }
        }
    }

    @Override
    public String getShortName() {
        return "zip_directory";
    }

    @Override
    public String getDescription(ExecutionContext context) {
        return String.format("zip -r %s %s", outputZipPath, inputDirectoryPath);
    }
}