Предупреждение при использовании FileChannel.transferTo() и Deflater.setInput(ByteBuffer): временный буфер, внутренне созданный FileChannel.transferTo(), очищается после использования (что просто означает, что позиция установлена на 0, предел=емкость и отсутствие байтов). удаляются).
Предполагая, что вы реализовали WritableByteChannel с помощью Deflater, это означает, что если вы вызовете deflater.setInput(tempbb) с помощью tempbb ByteBuffer, созданного filechannel.transferTo(writablechannel), вы заполняете входные данные размером с этот буфер (8192 или размер файла, если он меньше), остающийся в дефляторе.Когда вы закончите() и deflate() ваш Deflater, он подберет эти дополнительные входные байты и добавит их!
Обходной путь:
После того, как были введены все допустимые входные данные, всегда вызывайте deflater.setInput(ByteBuffer.allocate(0)) чтобы гарантировать, что вы не сможете добавлять больше байтов перед дефлятором. Finish() и далее deflater.deflate().
Вот пример программы, показывающий варианты. Просто нужен входной файл в качестве аргумента.
Достаточно даже короткого текстового файла в 1 строку.
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.StandardOpenOption;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
public class TestFileChannelTransferToDeflaterBug {
private static final ByteBuffer EMPTY_BB = ByteBuffer.allocate(0);
public static void main(String[] args) throws IOException {
System.out.println("Hex view with: od -A x -t x1z -v ");
int complevel = Deflater.NO_COMPRESSION;//DEFAULT_COMPRESSION still shows bug, harder to see
test(args, complevel, false, false);
test(args, complevel, true, false);
test(args, complevel, false, true);
test(args, complevel, true, true);
}
static void test(String[] args, int complevel, boolean useTrfTo, boolean fix) throws IOException {
String inFilename = args.length>0 ? args[0] : "x";//"README.md";
File f1 = new File(inFilename);
File f2 = new File(inFilename
+"-L"+(complevel0) {
long qty = inCh.transferTo(pos, remaining, gzChannel);
remaining -= qty;
pos += qty;
}
} else {//this does not produce excess bytes
long remaining = inCh.size();
ByteBuffer inBB = ByteBuffer.allocate(8192);//direct or not makes no difference
while(remaining>0) {
inBB.clear();
int qty = inCh.read(inBB);
remaining -= qty;
inBB.flip();
while(inBB.hasRemaining())
gzChannel.write(inBB);
}
}
gzChannel.finish();
System.out.println("\t"+f2.length()+" bytes. ");
}
}
}
}
public static class GzipWritableChannel implements WritableByteChannel {
private final Deflater deflater;
private final int compressionLevel;
private final boolean fix;
private final ByteBuffer workBuffer;
private final CRC32 crc = new CRC32();
private boolean closed;
private final WritableByteChannel out;
public GzipWritableChannel(WritableByteChannel out, int compressionLevel, boolean fix) throws IOException {
this.out = out;
this.compressionLevel = compressionLevel;
this.fix = fix;
if (out instanceof SelectableChannel && !((SelectableChannel)out).isBlocking())
throw new IllegalArgumentException("SelectableChannel in non-blocking mode not supported");
this.deflater = new Deflater(compressionLevel, true);
this.workBuffer = ByteBuffer.allocate(0xffff + 2);//direct or not makes no difference
writeHeader();
}
private void writeHeader() throws IOException {
workBuffer.clear().order(ByteOrder.LITTLE_ENDIAN);
workBuffer.putShort((short)GZIPInputStream.GZIP_MAGIC); //ID1 ID2
workBuffer.put((byte)Deflater.DEFLATED); //CM
workBuffer.put((byte)0);
workBuffer.putInt(0);//MTIME
workBuffer.put((byte)switch (compressionLevel) { //XFL
case Deflater.BEST_COMPRESSION -> 2;
case Deflater.BEST_SPEED -> 4;
default -> 0;
});
workBuffer.put((byte)-1); //OS
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
@Override
public boolean isOpen() {
return !closed;
}
@Override
public int write(ByteBuffer src) throws IOException {
if (deflater.finished())
throw new IOException("compression already finished");
int len = src.remaining();
if (len > 0) {
src.mark();
deflater.setInput(src);
while (!deflater.needsInput())
drainDeflaterOnce();
if(fix)
deflater.setInput(EMPTY_BB);
src.reset();
crc.update(src);
}
return len;
}
private void drainDeflaterOnce() throws IOException {
workBuffer.clear();
deflater.deflate(workBuffer);
workBuffer.flip();
while (workBuffer.hasRemaining()) //this is only efficient on blocking channel, else may spin fast...
out.write(workBuffer);
}
@Override
public void close() throws IOException {
if (closed)
return;
try {
finish();
} finally {
deflater.end();
out.close();
closed = true;
}
}
public void finish() throws IOException {
if (deflater.finished())
return;
deflater.finish();
while (!deflater.finished())
drainDeflaterOnce();
writeFooter();
}
private void writeFooter() throws IOException {
workBuffer.clear().order(ByteOrder.LITTLE_ENDIAN);
workBuffer.putInt((int)crc.getValue());
workBuffer.putInt(deflater.getTotalIn());
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/792 ... tbytebuffe
FileChannel.transferTo() очищает временный байтовый буфер, Deflater.setInput(ByteBuffer) сохраняет лишние нежелательные ⇐ JAVA
Программисты JAVA общаются здесь
1733408974
Anonymous
Предупреждение при использовании FileChannel.transferTo() и Deflater.setInput(ByteBuffer): временный буфер, внутренне созданный FileChannel.transferTo(), очищается после использования (что просто означает, что позиция установлена на 0, предел=емкость и отсутствие байтов). удаляются).
Предполагая, что вы реализовали WritableByteChannel с помощью Deflater, это означает, что если вы вызовете deflater.setInput(tempbb) с помощью tempbb ByteBuffer, созданного filechannel.transferTo(writablechannel), вы заполняете входные данные размером с этот буфер (8192 или размер файла, если он меньше), остающийся в дефляторе.Когда вы закончите() и deflate() ваш Deflater, [b]он подберет эти дополнительные входные байты и добавит их![/b]
Обходной путь:
После того, как были введены все допустимые входные данные, всегда вызывайте deflater.setInput(ByteBuffer.allocate(0)) чтобы гарантировать, что вы не сможете добавлять больше байтов перед дефлятором. Finish() и далее deflater.deflate().
Вот пример программы, показывающий варианты. Просто нужен входной файл в качестве аргумента.
Достаточно даже короткого текстового файла в 1 строку.
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.StandardOpenOption;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
public class TestFileChannelTransferToDeflaterBug {
private static final ByteBuffer EMPTY_BB = ByteBuffer.allocate(0);
public static void main(String[] args) throws IOException {
System.out.println("Hex view with: od -A x -t x1z -v ");
int complevel = Deflater.NO_COMPRESSION;//DEFAULT_COMPRESSION still shows bug, harder to see
test(args, complevel, false, false);
test(args, complevel, true, false);
test(args, complevel, false, true);
test(args, complevel, true, true);
}
static void test(String[] args, int complevel, boolean useTrfTo, boolean fix) throws IOException {
String inFilename = args.length>0 ? args[0] : "x";//"README.md";
File f1 = new File(inFilename);
File f2 = new File(inFilename
+"-L"+(complevel0) {
long qty = inCh.transferTo(pos, remaining, gzChannel);
remaining -= qty;
pos += qty;
}
} else {//this does not produce excess bytes
long remaining = inCh.size();
ByteBuffer inBB = ByteBuffer.allocate(8192);//direct or not makes no difference
while(remaining>0) {
inBB.clear();
int qty = inCh.read(inBB);
remaining -= qty;
inBB.flip();
while(inBB.hasRemaining())
gzChannel.write(inBB);
}
}
gzChannel.finish();
System.out.println("\t"+f2.length()+" bytes. ");
}
}
}
}
public static class GzipWritableChannel implements WritableByteChannel {
private final Deflater deflater;
private final int compressionLevel;
private final boolean fix;
private final ByteBuffer workBuffer;
private final CRC32 crc = new CRC32();
private boolean closed;
private final WritableByteChannel out;
public GzipWritableChannel(WritableByteChannel out, int compressionLevel, boolean fix) throws IOException {
this.out = out;
this.compressionLevel = compressionLevel;
this.fix = fix;
if (out instanceof SelectableChannel && !((SelectableChannel)out).isBlocking())
throw new IllegalArgumentException("SelectableChannel in non-blocking mode not supported");
this.deflater = new Deflater(compressionLevel, true);
this.workBuffer = ByteBuffer.allocate(0xffff + 2);//direct or not makes no difference
writeHeader();
}
private void writeHeader() throws IOException {
workBuffer.clear().order(ByteOrder.LITTLE_ENDIAN);
workBuffer.putShort((short)GZIPInputStream.GZIP_MAGIC); //ID1 ID2
workBuffer.put((byte)Deflater.DEFLATED); //CM
workBuffer.put((byte)0);
workBuffer.putInt(0);//MTIME
workBuffer.put((byte)switch (compressionLevel) { //XFL
case Deflater.BEST_COMPRESSION -> 2;
case Deflater.BEST_SPEED -> 4;
default -> 0;
});
workBuffer.put((byte)-1); //OS
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
@Override
public boolean isOpen() {
return !closed;
}
@Override
public int write(ByteBuffer src) throws IOException {
if (deflater.finished())
throw new IOException("compression already finished");
int len = src.remaining();
if (len > 0) {
src.mark();
deflater.setInput(src);
while (!deflater.needsInput())
drainDeflaterOnce();
if(fix)
deflater.setInput(EMPTY_BB);
src.reset();
crc.update(src);
}
return len;
}
private void drainDeflaterOnce() throws IOException {
workBuffer.clear();
deflater.deflate(workBuffer);
workBuffer.flip();
while (workBuffer.hasRemaining()) //this is only efficient on blocking channel, else may spin fast...
out.write(workBuffer);
}
@Override
public void close() throws IOException {
if (closed)
return;
try {
finish();
} finally {
deflater.end();
out.close();
closed = true;
}
}
public void finish() throws IOException {
if (deflater.finished())
return;
deflater.finish();
while (!deflater.finished())
drainDeflaterOnce();
writeFooter();
}
private void writeFooter() throws IOException {
workBuffer.clear().order(ByteOrder.LITTLE_ENDIAN);
workBuffer.putInt((int)crc.getValue());
workBuffer.putInt(deflater.getTotalIn());
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79255052/filechannel-transferto-clears-its-temp-bytebuffer-deflater-setinputbytebuffe[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия