FileChannel.transferTo() очищает временный байтовый буфер, Deflater.setInput(ByteBuffer) сохраняет лишние нежелательные JAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 FileChannel.transferTo() очищает временный байтовый буфер, Deflater.setInput(ByteBuffer) сохраняет лишние нежелательные

Сообщение Anonymous »

Предупреждение при использовании 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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»