org.nervousync.zip.io.CipherOutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utils-jdk11 Show documentation
Show all versions of utils-jdk11 Show documentation
Java utility collections, development by Nervousync Studio (NSYC)
/*
* Licensed to the Nervousync Studio (NSYC) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.nervousync.zip.io;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import org.nervousync.commons.core.Globals;
import org.nervousync.commons.core.zip.ZipOptions;
import org.nervousync.zip.crypto.Encryptor;
import org.nervousync.zip.crypto.impl.aes.AESEncryptor;
import org.nervousync.zip.crypto.impl.standard.StandardEncryptor;
import org.nervousync.zip.engine.AESEngine;
import org.nervousync.zip.models.AESExtraDataRecord;
import org.nervousync.zip.models.central.CentralDirectory;
import org.nervousync.zip.models.central.EndCentralDirectoryRecord;
import org.nervousync.zip.models.header.GeneralFileHeader;
import org.nervousync.zip.models.header.LocalFileHeader;
import org.nervousync.zip.models.header.utils.HeaderOperator;
import org.nervousync.exceptions.zip.ZipException;
import org.nervousync.zip.ZipFile;
import org.nervousync.utils.DateTimeUtils;
import org.nervousync.utils.FileUtils;
import org.nervousync.utils.RawUtils;
import org.nervousync.utils.StringUtils;
/**
* The type Cipher output stream.
*
* @author Steven Wee [email protected]
* @version $Revision : 1.0 $ $Date: Nov 29, 2017 2:39:25 PM $
*/
public class CipherOutputStream extends OutputStream {
/**
* Target output stream
*/
private final OutputStream outputStream;
/**
* Source file
*/
private File sourceFile;
private GeneralFileHeader generalFileHeader;
private LocalFileHeader localFileHeader;
private Encryptor encryptor;
/**
* The Zip options.
*/
ZipOptions zipOptions;
/**
* Target zip file object
*/
private final ZipFile zipFile;
/**
* CRC
*/
final CRC32 crc;
private long totalWriteBytes;
private long totalReadBytes;
/**
* The Bytes written for this file.
*/
long bytesWrittenForThisFile;
private final byte[] pendingBuffer;
private int pendingBufferLength;
/**
* Instantiates a new Cipher output stream.
*
* @param outputStream the output stream
* @param zipFile the zip file
*/
CipherOutputStream(OutputStream outputStream, ZipFile zipFile) {
this.outputStream = outputStream;
this.zipFile = zipFile;
this.initZipFile();
this.crc = new CRC32();
this.totalWriteBytes = 0L;
this.bytesWrittenForThisFile = 0L;
this.totalReadBytes = 0L;
this.pendingBuffer = new byte[Globals.AES_BLOCK_SIZE];
this.pendingBufferLength = 0;
}
/**
* Put the next entry.
*
* @param file the file
* @param zipOptions the zip options
* @throws ZipException the zip exception
*/
public void putNextEntry(File file, ZipOptions zipOptions) throws ZipException {
if (!zipOptions.isSourceExternalStream() && file == null) {
throw new ZipException("Input file is null!");
}
if (!zipOptions.isSourceExternalStream() && !FileUtils.isExists(file.getAbsolutePath())) {
throw new ZipException("Input file does not exists!");
}
try {
this.sourceFile = file;
this.zipOptions = (ZipOptions) zipOptions.clone();
if (this.zipOptions.isSourceExternalStream()) {
if (StringUtils.notBlank(this.zipOptions.getFileNameInZip())) {
if (this.zipOptions.getFileNameInZip().endsWith(Globals.ZIP_FILE_SEPARATOR)
|| this.zipOptions.getFileNameInZip().endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
this.zipOptions.setEncryptFiles(Boolean.FALSE);
this.zipOptions.setEncryptionMethod(Globals.ENC_NO_ENCRYPTION);
this.zipOptions.setCompressionMethod(Globals.COMP_STORE);
}
}
} else {
if (this.sourceFile.isDirectory()) {
this.zipOptions.setEncryptFiles(Boolean.FALSE);
this.zipOptions.setEncryptionMethod(Globals.ENC_NO_ENCRYPTION);
this.zipOptions.setCompressionMethod(Globals.COMP_STORE);
}
}
this.createGeneralFileHeaders();
this.createLocalFileHeaders();
if (this.zipFile.isSplitArchive()) {
if (this.zipFile.getCentralDirectory() == null
|| this.zipFile.getCentralDirectory().getFileHeaders() == null
|| this.zipFile.getCentralDirectory().getFileHeaders().size() == 0) {
byte[] intBuffer = new byte[4];
RawUtils.writeInt(intBuffer, RawUtils.Endian.LITTLE, (int) Globals.SPLITSIG);
this.outputStream.write(intBuffer);
this.totalWriteBytes += 4L;
}
}
if (this.outputStream instanceof SplitOutputStream) {
if (this.totalWriteBytes == 4) {
this.generalFileHeader.setOffsetLocalHeader(4L);
} else {
this.generalFileHeader
.setOffsetLocalHeader(((SplitOutputStream) this.outputStream).getFilePointer());
}
} else {
if (this.totalWriteBytes == 4) {
this.generalFileHeader.setOffsetLocalHeader(4L);
} else {
this.generalFileHeader.setOffsetLocalHeader(this.totalWriteBytes);
}
}
this.totalWriteBytes += this.writeLocalFileHeader(this.localFileHeader, this.outputStream);
if (this.zipOptions.isEncryptFiles()) {
this.initEncryptor();
if (this.encryptor != null) {
if (this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_STANDARD) {
byte[] headerBytes = ((StandardEncryptor) this.encryptor).getHeaderBytes();
this.outputStream.write(headerBytes);
this.totalWriteBytes += headerBytes.length;
this.bytesWrittenForThisFile += headerBytes.length;
} else if (this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
byte[] saltBytes = ((AESEncryptor) this.encryptor).getSaltBytes();
byte[] passwordVerifier = ((AESEncryptor) this.encryptor).getDerivedPasswordVerifier();
this.outputStream.write(saltBytes);
this.outputStream.write(passwordVerifier);
this.totalWriteBytes += saltBytes.length + passwordVerifier.length;
this.bytesWrittenForThisFile += saltBytes.length + passwordVerifier.length;
}
}
}
this.crc.reset();
} catch (ZipException e) {
throw e;
} catch (Exception e) {
throw new ZipException(e);
}
}
@Override
public void write(int b) throws IOException {
byte[] buffer = new byte[1];
buffer[0] = (byte) b;
this.write(buffer, 0, 1);
}
@Override
public void write(byte[] b) throws IOException {
if (b.length == 0) {
return;
}
this.write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return;
}
if (this.zipOptions.isEncryptFiles() && this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
if (this.pendingBufferLength != 0) {
if (len >= (Globals.AES_BLOCK_SIZE - this.pendingBufferLength)) {
System.arraycopy(b, off, this.pendingBuffer, this.pendingBufferLength,
(Globals.AES_BLOCK_SIZE - this.pendingBufferLength));
this.encryptAndWrite(this.pendingBuffer, 0, this.pendingBuffer.length);
off = (Globals.AES_BLOCK_SIZE - this.pendingBufferLength);
len -= off;
this.pendingBufferLength = 0;
} else {
System.arraycopy(b, off, this.pendingBuffer, this.pendingBufferLength, len);
this.pendingBufferLength += len;
return;
}
}
if (len % 16 != 0) {
System.arraycopy(b, (len + off) - (len % 16), this.pendingBuffer, 0, len % 16);
this.pendingBufferLength = len % 16;
len -= this.pendingBufferLength;
}
}
if (len != 0) {
this.encryptAndWrite(b, off, len);
}
}
/**
* Close entry.
*
* @throws IOException the io exception
* @throws ZipException the zip exception
*/
public void closeEntry() throws IOException, ZipException {
if (this.pendingBufferLength != 0) {
this.encryptAndWrite(this.pendingBuffer, 0, this.pendingBufferLength);
this.pendingBufferLength = 0;
}
if (this.zipOptions.isEncryptFiles() && this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
if (this.encryptor instanceof AESEncryptor) {
this.outputStream.write(((AESEncryptor) this.encryptor).getFinalMac());
this.bytesWrittenForThisFile += 10;
this.totalWriteBytes += 10;
} else {
throw new ZipException("invalid encryptor for AES encrypted file");
}
}
this.generalFileHeader.setCompressedSize(this.bytesWrittenForThisFile);
this.localFileHeader.setCompressedSize(this.bytesWrittenForThisFile);
if (this.zipOptions.isSourceExternalStream()) {
this.generalFileHeader.setOriginalSize(this.totalReadBytes);
if (this.localFileHeader.getOriginalSize() != this.totalReadBytes) {
this.localFileHeader.setOriginalSize(this.totalReadBytes);
}
}
long crc32 = this.crc.getValue();
if (this.generalFileHeader.isEncrypted()
&& this.generalFileHeader.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
crc32 = 0L;
}
this.generalFileHeader.setCrc32(crc32);
this.localFileHeader.setCrc32(crc32);
this.zipFile.getLocalFileHeaderList().add(this.localFileHeader);
this.zipFile.getCentralDirectory().getFileHeaders().add(this.generalFileHeader);
this.totalWriteBytes +=
HeaderOperator.writeExtendedLocalHeader(this.localFileHeader, this.outputStream);
this.crc.reset();
this.bytesWrittenForThisFile = 0L;
this.encryptor = null;
this.totalReadBytes = 0L;
}
/**
* Finish.
*
* @throws ZipException the zip exception
*/
public void finish() throws ZipException {
this.zipFile.getEndCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory(this.totalWriteBytes);
this.zipFile.finalizeZipFile(this.outputStream);
}
public void close() throws IOException {
if (this.outputStream != null) {
this.outputStream.close();
}
}
/**
* Update total bytes read.
*
* @param readCount the read count
*/
void updateTotalBytesRead(int readCount) {
if (readCount > 0) {
this.totalReadBytes += readCount;
}
}
private void encryptAndWrite(byte[] b, int off, int len) throws IOException {
if (this.encryptor != null) {
try {
this.encryptor.encryptData(b, off, len);
} catch (ZipException e) {
throw new IOException(e);
}
}
this.outputStream.write(b, off, len);
this.totalWriteBytes += len;
this.bytesWrittenForThisFile += len;
}
private void initZipFile() {
if (this.zipFile.getEndCentralDirectoryRecord() == null) {
this.zipFile.setEndCentralDirectoryRecord(new EndCentralDirectoryRecord());
}
if (this.zipFile.getCentralDirectory() == null) {
this.zipFile.setCentralDirectory(new CentralDirectory());
}
if (this.zipFile.getCentralDirectory().getFileHeaders() == null) {
this.zipFile.getCentralDirectory().setFileHeaders(new ArrayList<>());
}
if (this.zipFile.getLocalFileHeaderList() == null) {
this.zipFile.setLocalFileHeaderList(new ArrayList<>());
}
if (this.outputStream instanceof SplitOutputStream) {
if (((SplitOutputStream) this.outputStream).isSplitZipFile()) {
this.zipFile.setSplitArchive(true);
this.zipFile.setSplitLength(((SplitOutputStream) this.outputStream).getSplitLength());
}
}
this.zipFile.getEndCentralDirectoryRecord().setSignature(Globals.ENDSIG);
}
private void createGeneralFileHeaders() throws ZipException {
this.generalFileHeader = new GeneralFileHeader();
this.generalFileHeader.setSignature((int) Globals.CENSIG);
this.generalFileHeader.setMadeVersion(20);
this.generalFileHeader.setExtractNeeded(20);
if (this.zipOptions.isEncryptFiles() && this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
this.generalFileHeader.setCompressionMethod(Globals.ENC_METHOD_AES);
this.generateAESExtraDataRecord();
} else {
this.generalFileHeader.setCompressionMethod(this.zipOptions.getCompressionMethod());
}
if (this.zipOptions.isEncryptFiles()) {
this.generalFileHeader.setEncrypted(true);
this.generalFileHeader.setEncryptionMethod(this.zipOptions.getEncryptionMethod());
}
String entryPath;
if (this.zipOptions.isSourceExternalStream()) {
this.generalFileHeader.setLastModFileTime((int) DateTimeUtils.toDosTime(System.currentTimeMillis()));
if (this.zipOptions.getFileNameInZip() == null || this.zipOptions.getFileNameInZip().length() == 0) {
throw new ZipException("fileNameInZip is null or empty");
}
entryPath = this.zipOptions.getFileNameInZip();
} else {
this.generalFileHeader.setLastModFileTime(
(int) DateTimeUtils.toDosTime(FileUtils.lastModify(this.sourceFile.getAbsolutePath())));
this.generalFileHeader.setOriginalSize(this.sourceFile.length());
entryPath = ZipFile.getRelativeFileName(this.sourceFile.getAbsolutePath(),
this.zipOptions.getRootFolderInZip(), this.zipOptions.getDefaultFolderPath());
}
if (entryPath == null || entryPath.length() == 0) {
throw new ZipException("fileName is null or empty. unable to create file header");
}
this.generalFileHeader.setEntryPath(entryPath);
if (StringUtils.notBlank(this.zipFile.getCharsetEncoding())) {
this.generalFileHeader.setFileNameLength(
StringUtils.encodedStringLength(entryPath, this.zipFile.getCharsetEncoding()));
} else {
this.generalFileHeader.setFileNameLength(StringUtils.encodedStringLength(entryPath));
}
if (this.outputStream instanceof SplitOutputStream) {
this.generalFileHeader
.setDiskNumberStart(((SplitOutputStream) this.outputStream).getCurrentSplitFileIndex());
} else {
this.generalFileHeader.setDiskNumberStart(0);
}
int fileAttrs = 0;
if (!this.zipOptions.isSourceExternalStream()) {
fileAttrs = this.getFileAttributes(this.sourceFile);
}
byte[] externalFileAttrs = { (byte) fileAttrs, 0, 0, 0 };
this.generalFileHeader.setExternalFileAttr(externalFileAttrs);
boolean isDirectory;
if (this.zipOptions.isSourceExternalStream()) {
isDirectory = entryPath.endsWith(Globals.ZIP_FILE_SEPARATOR)
|| entryPath.endsWith(Globals.DEFAULT_PAGE_SEPARATOR);
} else {
isDirectory = this.sourceFile.isDirectory();
}
this.generalFileHeader.setDirectory(isDirectory);
if (this.generalFileHeader.isDirectory()) {
this.generalFileHeader.setCompressedSize(0L);
this.generalFileHeader.setOriginalSize(0L);
} else {
if (!this.zipOptions.isSourceExternalStream()) {
long fileSize = FileUtils.fileSize(this.sourceFile);
if (this.zipOptions.getCompressionMethod() == Globals.COMP_STORE) {
if (this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_STANDARD) {
this.generalFileHeader.setCompressedSize(fileSize + Globals.STD_DEC_HDR_SIZE);
} else if (this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_AES) {
int saltLength;
switch (this.zipOptions.getAesKeyStrength()) {
case Globals.AES_STRENGTH_128:
saltLength = 8;
break;
case Globals.AES_STRENGTH_256:
saltLength = 16;
break;
default:
throw new ZipException("invalid aes key strength, cannot determine key sizes");
}
this.generalFileHeader
.setCompressedSize(fileSize + saltLength + Globals.AES_AUTH_LENGTH + 2);
} else {
this.generalFileHeader.setCompressedSize(0L);
}
} else {
this.generalFileHeader.setCompressedSize(0L);
}
this.generalFileHeader.setOriginalSize(fileSize);
}
}
if (this.zipOptions.isEncryptFiles()
&& this.zipOptions.getEncryptionMethod() == Globals.ENC_METHOD_STANDARD) {
this.generalFileHeader.setCrc32(this.zipOptions.getSourceFileCRC());
}
byte[] generalPurposeFlag = new byte[2];
int[] bitArray = this.generateGeneralPurposeBitArray(this.generalFileHeader.isEncrypted(),
this.zipOptions.getCompressionMethod());
generalPurposeFlag[0] = RawUtils.bitArrayToByte(bitArray);
boolean isFileNameCharset = StringUtils.notBlank(this.zipFile.getCharsetEncoding());
if ((isFileNameCharset && this.zipFile.getCharsetEncoding().equalsIgnoreCase(Globals.DEFAULT_ENCODING))
|| (!isFileNameCharset && StringUtils.detectCharset(this.generalFileHeader.getEntryPath())
.equalsIgnoreCase(Globals.DEFAULT_ENCODING))) {
generalPurposeFlag[1] = 8;
}
this.generalFileHeader.setGeneralPurposeFlag(generalPurposeFlag);
}
private void generateAESExtraDataRecord() throws ZipException {
if (this.zipOptions == null) {
throw new ZipException("zip parameters are null, cannot generate AES Extra Data record");
}
AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord();
aesExtraDataRecord.setSignature(Globals.AESSIG);
aesExtraDataRecord.setDataSize(7);
aesExtraDataRecord.setVendorID("AE");
aesExtraDataRecord.setVersionNumber(2);
if (this.zipOptions.getAesKeyStrength() == Globals.AES_STRENGTH_128) {
aesExtraDataRecord.setAesStrength(Globals.AES_STRENGTH_128);
} else if (this.zipOptions.getAesKeyStrength() == Globals.AES_STRENGTH_256) {
aesExtraDataRecord.setAesStrength(Globals.AES_STRENGTH_256);
} else {
throw new ZipException("invalid AES key strength, cannot generate AES Extra data record");
}
aesExtraDataRecord.setCompressionMethod(this.zipOptions.getCompressionMethod());
this.generalFileHeader.setAesExtraDataRecord(aesExtraDataRecord);
}
private int getFileAttributes(File file) throws ZipException {
if (file == null) {
throw new ZipException("input file is null, cannot get file attributes");
}
if (!file.exists()) {
return 0;
}
if (file.isDirectory()) {
return Globals.FOLDER_MODE_NONE;
} else {
if (!file.canWrite()) {
return Globals.FILE_MODE_READ_ONLY;
} else {
return Globals.FILE_MODE_NONE;
}
}
}
private int[] generateGeneralPurposeBitArray(boolean isEncrypted, int compressionMethod) {
int[] generalPurposeFlag = new int[8];
if (isEncrypted) {
generalPurposeFlag[0] = 1;
} else {
generalPurposeFlag[0] = 0;
}
if (compressionMethod != Globals.COMP_DEFLATE) {
generalPurposeFlag[1] = 0;
generalPurposeFlag[2] = 0;
}
generalPurposeFlag[3] = 1;
return generalPurposeFlag;
}
private void createLocalFileHeaders() throws ZipException {
if (this.generalFileHeader == null) {
throw new ZipException("file header is null, cannot create local file header");
}
this.localFileHeader = new LocalFileHeader();
this.localFileHeader.setSignature((int) Globals.LOCSIG);
this.localFileHeader.setExtractNeeded(this.generalFileHeader.getExtractNeeded());
this.localFileHeader.setCompressionMethod(this.generalFileHeader.getCompressionMethod());
this.localFileHeader.setLastModFileTime(this.generalFileHeader.getLastModFileTime());
this.localFileHeader.setOriginalSize(this.generalFileHeader.getOriginalSize());
this.localFileHeader.setFileNameLength(this.generalFileHeader.getFileNameLength());
this.localFileHeader.setEntryPath(this.generalFileHeader.getEntryPath());
this.localFileHeader.setEncrypted(this.generalFileHeader.isEncrypted());
this.localFileHeader.setEncryptionMethod(this.generalFileHeader.getEncryptionMethod());
this.localFileHeader.setAesExtraDataRecord(this.generalFileHeader.getAesExtraDataRecord());
this.localFileHeader.setCrc32(this.generalFileHeader.getCrc32());
this.localFileHeader.setCompressedSize(this.generalFileHeader.getCompressedSize());
this.localFileHeader.setGeneralPurposeFlag(this.generalFileHeader.getGeneralPurposeFlag().clone());
}
private void initEncryptor() throws ZipException {
if (this.zipOptions.isEncryptFiles()) {
switch (this.zipOptions.getEncryptionMethod()) {
case Globals.ENC_METHOD_STANDARD:
this.encryptor = new StandardEncryptor(this.zipOptions.getPassword(),
(this.localFileHeader.getLastModFileTime() & 0x0000FFFF) << 16);
break;
case Globals.ENC_METHOD_AES:
this.encryptor = new AESEncryptor(this.zipOptions.getPassword(), this.zipOptions.getAesKeyStrength());
break;
default:
throw new ZipException("invalid encryption method");
}
} else {
this.encryptor = null;
}
}
private int writeLocalFileHeader(LocalFileHeader localFileHeader,
OutputStream outputStream) throws ZipException {
if (localFileHeader == null) {
throw new ZipException("Local file header is null, cannot write!");
}
try {
List headerBytesList = new ArrayList<>();
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
byte[] emptyLongBuffer = { 0, 0, 0, 0, 0, 0, 0, 0 };
RawUtils.writeInt(intBuffer, RawUtils.Endian.LITTLE, localFileHeader.getSignature());
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) localFileHeader.getExtractNeeded());
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
HeaderOperator.copyByteArrayToList(localFileHeader.getGeneralPurposeFlag(), headerBytesList);
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) localFileHeader.getCompressionMethod());
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
RawUtils.writeInt(intBuffer, RawUtils.Endian.LITTLE, localFileHeader.getLastModFileTime());
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
RawUtils.writeInt(intBuffer, RawUtils.Endian.LITTLE, (int) localFileHeader.getCrc32());
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
boolean writingZip64Record = Boolean.FALSE;
long originalSize = localFileHeader.getOriginalSize();
if (originalSize + Globals.ZIP64_EXTRA_BUFFER_SIZE >= Globals.ZIP_64_LIMIT) {
RawUtils.writeLong(longBuffer, RawUtils.Endian.LITTLE, Globals.ZIP_64_LIMIT);
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
writingZip64Record = true;
localFileHeader.setWriteCompressSizeInZip64ExtraRecord(true);
} else {
RawUtils.writeLong(longBuffer, RawUtils.Endian.LITTLE, localFileHeader.getCompressedSize());
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
RawUtils.writeLong(longBuffer, RawUtils.Endian.LITTLE, localFileHeader.getOriginalSize());
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToList(intBuffer, headerBytesList);
localFileHeader.setWriteCompressSizeInZip64ExtraRecord(Boolean.FALSE);
}
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) localFileHeader.getFileNameLength());
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
int extraFieldLength = 0;
if (writingZip64Record) {
extraFieldLength += 20;
}
if (localFileHeader.getAesExtraDataRecord() != null) {
extraFieldLength += 11;
}
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) extraFieldLength);
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
if (StringUtils.notBlank(this.zipFile.getCharsetEncoding())) {
byte[] fileNameBytes = localFileHeader.getEntryPath().getBytes(this.zipFile.getCharsetEncoding());
HeaderOperator.copyByteArrayToList(fileNameBytes, headerBytesList);
} else {
HeaderOperator.copyByteArrayToList(localFileHeader.getEntryPath(), headerBytesList);
}
if (writingZip64Record) {
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) Globals.EXTRAFIELDZIP64LENGTH);
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
RawUtils.writeShort(shortBuffer, RawUtils.Endian.LITTLE, (short) 16);
HeaderOperator.copyByteArrayToList(shortBuffer, headerBytesList);
RawUtils.writeLong(longBuffer, RawUtils.Endian.LITTLE, localFileHeader.getOriginalSize());
HeaderOperator.copyByteArrayToList(longBuffer, headerBytesList);
HeaderOperator.copyByteArrayToList(emptyLongBuffer, headerBytesList);
}
if (localFileHeader.getAesExtraDataRecord() != null) {
AESEngine.processHeader(localFileHeader.getAesExtraDataRecord(), headerBytesList);
}
byte[] bytes = HeaderOperator.convertByteArrayListToByteArray(headerBytesList);
outputStream.write(bytes);
outputStream.flush();
return bytes.length;
} catch (Exception e) {
throw new ZipException(e);
}
}
}