java использует сжатие и распаковку 7z

Java задняя часть Google модульный тест
java использует сжатие и распаковку 7z

java использует сжатие и распаковку 7z

Зависит отFind· Дата выпуска· обновлено27 просмотрено

1. Introduction

Два года назад, когда я хотел написать небольшую функцию распаковки в java-проекте, я использовал7zip bindingпроект, и запись в блоге была выпущена одновременноJava распаковать файл 7z, я не ожидал, что статья хорошо посещается и занимает высокие позиции в результатах поиска Google и Baidu, хотя более вероятная причина в том, что никто ничего не делает в этой области.

Некоторое время назад я работал над проектом, и мне нужно было использовать функцию сжатия и распаковки, но после тщательного изучения я обнаружил, что этот проект давно не обновлялся (хотя на странице проекта указано Последнее обновление: 2017- 07-19, но последнее изменение кода — 2015), и есть довольно много проблем с использованием. Поэтому я обратился за помощью к официальному проекту 7z, и обнаружил, что у 7zip официально есть LZMA SDK, но его Java часть может долго не обновляться по комментариям, а потому что код относительно прост, и примеры примеров есть , я решил использовать его для разработки. .

2. Адрес проекта

Я извлек официальную часть Java и добавил свою молнию.

адрес проекта:LZMA

Написал простой пример того, как разбить большой файл на более мелкие фрагменты и сжать каждый фрагмент вместо сжатия всего файла:

LZMA Example

3. Запустите официальный образец кода

В официальном примере LzmaAlone — это независимый класс, содержащий основную функцию.

Код очень короткий. Вы можете видеть, что это в основном парсинг параметров командной строки. Его использование:


"\nUsage:  LZMA <e|d> [<switches>...] inputFile outputFile\n" +
  "  e: encode file\n" +
  "  d: decode file\n" +
  "  b: Benchmark\n" +
  "<Switches>\n" +
  // "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n" +
  "  -d{N}:  set dictionary - [0,28], default: 23 (8MB)\n" +
  "  -fb{N}: set number of fast bytes - [5, 273], default: 128\n" +
  "  -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
  "  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
  "  -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
  "  -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
  "  -eos:   write End Of Stream marker\n"
  );

3.1 encode file part


if (params.Command == CommandLine.kEncode)
{
  Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
  if (!encoder.SetAlgorithm(params.Algorithm))
    throw new Exception("Incorrect compression mode");
  if (!encoder.SetDictionarySize(params.DictionarySize))
    throw new Exception("Incorrect dictionary size");
  if (!encoder.SetNumFastBytes(params.Fb))
    throw new Exception("Incorrect -fb value");
  if (!encoder.SetMatchFinder(params.MatchFinder))
    throw new Exception("Incorrect -mf value");
  if (!encoder.SetLcLpPb(params.Lc, params.Lp, params.Pb))
    throw new Exception("Incorrect -lc or -lp or -pb value");
  encoder.SetEndMarkerMode(eos);
  //写入5bytes配置参数
  encoder.WriteCoderProperties(outStream);
  
  
  long fileSize;
  if (eos)
    fileSize = -1;
  else
    fileSize = inFile.length();
  for (int i = 0; i < 8; i++)
    outStream.write((int)(fileSize >>> (8 * i)) & 0xFF);
  encoder.Code(inStream, outStream, -1, -1, null);
}

Предыдущая часть — установка параметров сжатия.

Раздел алгоритма сжатия


	public boolean SetAlgorithm(int algorithm)
	{
		/*
		_fastMode = (algorithm == 0);
		_maxMode = (algorithm >= 2);
		*/
		return true;
	}

Может потому что код давно не обновлялся, java версия кода, алгоритм сжатия тоже прямо опущен какmaxMode. Затем содержимое настройки, непосредственно связанное с параметром, записывается в выходной поток, всего 5 байтов.

fileSizeчерез файловый потокlength()полученная функция. И записывайте его байт за байтом в выходной поток. Обратите внимание, что тип здесьlong, но при записи он принимает int. Здесь нет проблем. потому что>>>является логическим сдвигом вправо, без знака, даже если принудительно усечен доint, так как только0xFF8 бит, потому что не будет проблемы недостаточной точности.

3.2 decode part


int propertiesSize = 5;
byte[] properties = new byte[propertiesSize];
if (inStream.read(properties, 0, propertiesSize) != propertiesSize)
  throw new Exception("input .lzma file is too short");
Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
if (!decoder.SetDecoderProperties(properties))
  throw new Exception("Incorrect stream properties");
long outSize = 0;
for (int i = 0; i < 8; i++)
{
  int v = inStream.read();
  if (v < 0)
    throw new Exception("Can't read stream size");
  outSize |= ((long)v) << (8 * i);
}
if (!decoder.Code(inStream, outStream, outSize))
  throw new Exception("Error in data stream");

Первые 5 прочитанных байт — это параметры сжатия, хранящиеся в энкоде, при распаковке указывать их не нужно, их можно прочитать прямо из сжатого файла.

Следующий цикл for определяет размер распакованного исходного файла, сохраняемого перед чтением. Принцип тот же, что и выше.

4. Сжатие реконструкции

Текущее требование состоит в том, чтобы завершить свои собственные функции кодирования и декодирования.byte[]По мере сжатия контента сжатый контент помещается в другойbyte[]внутри. Таким образом, он может адаптироваться к сжатию и распаковке данных в нефайловых сценариях.

4.1 Zipper.javaкодировать содержимое:


/**
     * 压缩函数
     *
     * @param inputStream:  要压缩的数据,如果源数据是byte[],那么在外层要ByteArrayInputStream ins = new ByteArrayInputStream(yourbytes[], offset, effective_length)
     * @param outputStream: 压缩好的数据,可以通过outputs_bytes.toBytes变成byte数组
     * @param len:          inputs_bytes中的有效数据长度,用于写入压缩好的数据开头,解压时会先读出来这段大小,以便知道要解压的数据大小。注意数据类型
     */
public void encode(ByteArrayInputStream inputStream, ByteArrayOutputStream outputStream, long len) throws Exception {
  Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
  //设置压缩参数。默认即可
  if (!encoder.SetAlgorithm(2))
    throw new Exception("Incorrect compression mode");
  if (!encoder.SetDictionarySize(1 << 23))
    throw new Exception("Incorrect dictionary size");
  if (!encoder.SetNumFastBytes(128))
    throw new Exception("Incorrect -fb value");
  if (!encoder.SetMatchFinder(1))
    throw new Exception("Incorrect -mf value");
  if (!encoder.SetLcLpPb(3, 0, 2))
    throw new Exception("Incorrect -lc or -lp or -pb value");
  encoder.SetEndMarkerMode(false);
  //首先会有5bytes的参数信息被写入
  encoder.WriteCoderProperties(outputStream);
  //接下来8bytes是要压缩的数据的长度,在解压时将被读取。注意这里len是long类型,如果是int,则最大可表示2GB的数据,因此采用long,但是里面每个byte在存储的时候,使用int即可。
  for (int j = 0; j < 8; j++)
    //无符号右移
    outputStream.write((int) (len >>> (8 * j)) & 0xFF);
  // inSize、outSize以及progress参数可以这样设置不用理会
  encoder.Code(inputStream, outputStream, -1, -1, null);
}

4.2 Тестовое чтение файла и разделение сжатой части


Zipper zipper = new Zipper();

//读取文件
File infile = new File("/home/find/ddown/aa/aa.pptx");
BufferedInputStream ins = new BufferedInputStream(new FileInputStream(infile));
BufferedOutputStream outs = new BufferedOutputStream(new FileOutputStream(new File("/home/find/ddown/aa/aa.lzma")));
// @todo 设置real_len为int,实际限制了每块的大小不能超过2GB
int real_len;
// 要将文件分割的文件块的大小
final int blockSize = 1024 << 3;
// 用来保存每块压缩大小
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < infile.length(); i += real_len) {
  //由于压缩可能会导致文件块变大,因此预开辟两倍空间存放,默认文件分块大小为8KB,即1024<<3
  byte[] inbytes = new byte[blockSize << 1];
  // @todo: 如果实际不是读到1024 × 8,除非到文件尾部,否则应该继续读取,直到读完1024*8长度的块。
  real_len = ins.read(inbytes, 0, blockSize);
  // @warning: 一定要注意,要以实际大小建stream!!!否则压缩时,会将实际有效数据后面的部分空数据也认为是有效的。!!!
  ByteArrayInputStream inputStream = new ByteArrayInputStream(inbytes, 0, real_len);
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream(blockSize << 1);
  System.out.print("实际读取的字节数" + real_len);
  zipper.encode(inputStream, outputStream, (long) real_len);
  // ByteArrarInputStream.size()是指实际有效数据
  queue.offer(outputStream.size());
  System.out.println("压缩后大小" + (outputStream.size()));
  //将压缩好的数据写入压缩文件
  outs.write(outputStream.toByteArray());
}
System.out.println("encode end\n======================================\n");

zipper просто инкапсулирует кодировку.

Как вы можете видеть в тестовом разделе, прочитайте содержимое файла, чтобыbyte[] inbytesв, сByteArrayInputStreamУпакуйте его в поток и передайте в функцию кодирования.

Обратите внимание, что для хранения сжатого размера каждого блока создается новая очередь, потому что в тесте файл разбивается на данные о размере блока для сжатия, а затем записывается в файл, а часть распаковки читает файл в соответствии с ранее хранимых данных.Создайте новый поток со сжатым размером и распакуйте блок.

4.3 Несколько ключевых вопросов

  • читать в файлеreadСодержимое, фактически прочитанное функцией, может не совпадать по размеру с длиной входящего запроса.
  • в новомByteArrayInputStreamвремя, должноУстановите размер на размер фактического блока сжатых данных, в противном случае функция сжатия исходного звука 7zip будет считать действительными данные часть, превышающую реальный допустимый размер данных! В результате ввод на самом деле больше, чем вы просили для сжатия, и естественный результат сжатия неверен.
  • При сжатии каждого блока файла параметры сжатия и размер файла в распакованном виде сохраняются.

5. Модернизация и декомпрессия

5.1 Разархивируйте код


/**
     * 解压函数
     *
     * @param inputStream:  要解压的数据。要求同encode。
     * @param outputStream: 解压获得的数据。
     */
public void decode(ByteArrayInputStream inputStream, ByteArrayOutputStream outputStream) throws Exception {
  Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
  //先读取5bytes设置
  int propertiesSize = 5;
  byte[] properties = new byte[propertiesSize];
  if (inputStream.read(properties, 0, propertiesSize) != propertiesSize)
    throw new Exception("input .lzma file is too short");
  if (!decoder.SetDecoderProperties(properties))
    throw new Exception("Incorrect stream properties");
  long outSize = 0;
  // 读取8bytes的要解压出来的文件长度(单位bytes)
  for (int j = 0; j < 8; j++) {
    int v = inputStream.read();
    if (v < 0)
      throw new Exception("Can't read stream size");
    outSize |= ((long) v) << (8 * j);
  }
  if (!decoder.Code(inputStream, outputStream, outSize)) {
    throw new Exception("Error in data stream");
  }
  //@todo: 这里不应该只是打印,应该throw error
  if (outputStream.size() != outSize) {
    System.out.println("实际解压大小和记录不同 outputstream.size " + outputStream.size() + "\toutsize" + outSize);
  }
}

5.2 Тестирование


Zipper zipper = new Zipper();
// decoder part
infile = new File("/home/find/ddown/aa/aa.lzma");
BufferedOutputStream o2 = new BufferedOutputStream(new FileOutputStream(new File("/home/find/ddown/aa/aa_extra.pptx")));
BufferedInputStream i2 = new BufferedInputStream(new FileInputStream(infile));
// 每个压缩块的大小都在queue里。一个一个压缩块的进行读取和解压
while (!queue.isEmpty()) {
  byte[] inbytes = new byte[blockSize << 1];
  real_len = i2.read(inbytes, 0, queue.peek());
  //@todo: 这里应该throw error
  if (real_len != queue.peek()) {
    System.out.println("读取的大小和队列里的大小(要读的大小)不同" + real_len + "\t" + queue.peek());
  }

  ByteArrayInputStream inputStream = new ByteArrayInputStream(inbytes, 0, queue.peek());
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream(blockSize << 1);
  zipper.decode(inputStream, outputStream);
  o2.write(outputStream.toByteArray());

  queue.poll();
}
o2.flush();
o2.close();
i2.close();

Авторское право на статью FindHao Все 丨 На этом сайте используются настройки по умолчаниюCC-BY-NC-SA 4.0Соглашение об авторизации|
Перепечатка должна включать это заявление и гиперссылку на автора. FindHao И оригинальный адрес этой статьи:
Woohoo. Найдите good.net/easy coding/…

Вам может понравиться: (Рекомендации по похожему контенту и рекламные объявления используют систему рекомендаций Google, и вам необходимо разблокировать сайт для показа рекламы. Спасибо, что нажали ↓Реклама для поддержки блоггеров~)