Форум: "Прочее";
Текущий архив: 2017.07.16;
Скачать: [xml.tar.bz2];
ВнизZlib - TZDecompressionStream.Seek написан криво Найти похожие ветки
← →
Кто б сомневался © (2016-05-22 06:34) [0]Или не используйте метод TZipFile.Read(out Stream: TStream;), который с потоком.
Те кто использует Zlib - TZDecompressionStream и TZipFile, а он использует TZDecompressionStream - у тех файлы могут распаковываться в 2 раза медленнее.
Просто потому что они распаковываются по 2 раза (XE5).
Возможно у кого то есть XE 8, можете выложить куда нибудь System.ZLib.pas? Возможно там исправили баг...
Просьба прокомментировать.. Пол ночи протрассировал :)
Вобщем в чем суть:var
Zip:TZipFile;
AbstractStream: TStream;
LocalHeader: TZipHeader;
OutputStream: TStream;
begin
OutputStream := TFileStream.Create("d:\123.123", fmCreate);
Zip:=TZipFile.Create;
zip.Open("d:\321\321.ZIP",zmRead);
// вытаскиваем из зипа файл section9.xhtml
zip.Read("section9.xhtml", AbstractStream, LocalHeader);
Read возвращает только абстрактный TStream, который создает у себя.
Честно говоря такой вариант мне тоже не очень понятен, т.к. придется все равно копировать данные оттуда. С какой целью это сделано непонятно? ..
По факту там создается Result := TZDecompressionStream.Create(InStream, -15); из модуля Zlib.
Дальше копируем с него данные. Ведь по другому его никак не забрать (мне файл нужен только в памяти)..
OutputStream.CopyFrom(AbstractStream, AbstractStream.Size);
И вот теперь самое интересное:
Разберем строчку:
OutputStream.CopyFrom(AbstractStream, AbstractStream.Size);
Сначала запрашивается AbstractStream.Size .
Т.е. получаем:function TStream.GetSize: Int64;
var
Pos: Int64;
begin
Pos := Seek(0, soCurrent);
Result := Seek(0, soEnd); <<<<<
Seek(Pos, soBeginning);
end;
Это значит что для того, чтобы получить размер, данные нужно полностью распаковать . Вот на этом этапе (выделил стрелкой) они распаковываются.. в локальный буфер.. Весь файл полностью будет распакован.
Почему блин буфер локальный и что мешало сделать его уровня класса, я незнаю...function TZDecompressionStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
const
BufSize = 8192;
var
buf: TBytes; <<<<<<<<<<<<<<<<<<<<
i: Integer;
localOffset: Int64;
begin
if (Offset = 0) and (Origin = soBeginning) then
begin
ZDecompressCheck(inflateReset(FZStream));
FZStream.next_in := @FBuffer;
FZStream.avail_in := 0;
FStream.Position := FStreamStartPos;
FStreamPos := FStreamStartPos;
end
else if ((Offset >= 0) and (Origin = soCurrent)) or
(((NativeUInt(offset) - FZStream.total_out) > 0) and (Origin = soBeginning)) then
begin
localOffset := Offset;
if (Origin = soBeginning) then Dec(localOffset, FZStream.total_out);
if localOffset > 0 then
begin
SetLength(buf, BufSize);
for i := 1 to localOffset div BufSize do ReadBuffer(buf, BufSize);
ReadBuffer(buf, localOffset mod BufSize);
end;
end
else if (Offset = 0) and (Origin = soEnd) then
begin
SetLength(buf, BufSize);
while Read(buf, 0, BufSize) > 0 do ;
end
else raise EZDecompressionError.Create(SZInvalid);
result := FZStream.total_out;
end;
Ну дальше вы уже догадались:
OutputStream.CopyFrom(AbstractStream, AbstractStream.Size);
Затем после GetSize идет обычный вызов ReadBuffer.
Итого, файл опять распаковается повторно.
Более того, если вы читаете с CopyFrom порциями (допустим вместо AbstractStream.Size - поставите свой размер буфера) - то тогда файл будет распаковываться полностью за один запрос КАЖДЫЙ РАЗ.
Если в XE8 не исправили такой жирный баг, тогда было бы неплохо запостить его в QC (я не смогу)..
Если нужно прочитать как следует (распаковав один раз файл), то лучше использовать :
TZipFile.Read(const FileName: string; out Bytes: TBytes);
← →
Кто б сомневался © (2016-05-22 06:40) [1]Да, а вы спросите, как же задается размер буфера в этом методе
TZipFile.Read(const FileName: string; out Bytes: TBytes);
он достается из заранее сохраненного поля UncompressedSize - у зипа есть такое поле в структуре каждого запакованного файла в TZipHeader.
← →
Кто б сомневался © (2016-05-22 07:22) [2]А вот например метод
TZipFile.Extract(const FileName: string; const Path: string; CreateSubDirs: Boolean);
Тут ребята исхитрились, и сделали так:LOutStream.CopyFrom(LInStream, FFiles[Index].UncompressedSize);
Тот самое поле из структуры.. При этом FFiles находится в привате.
← →
Rouse_ © (2016-05-22 15:23) [3]Я вообще свой использую: http://rouse.drkb.ru/components.php#fwzip
Оригинальный который в Delphi - не фонтан.
← →
K-1000 © (2016-05-22 15:53) [4]
> Rouse_ © (22.05.16 15:23) [3]
> Я вообще свой использую: http://rouse.drkb.ru/components.
> php#fwzip
> Оригинальный который в Delphi - не фонтан.
Почему?
← →
Кто б сомневался © (2016-05-22 17:11) [5]
> K-1000 © (22.05.16 15:53) [4]
Тут имеется в виду TZipFile из zip.
Не фонтан он потому, что целиком выделяет буфер для всего распакованного файла.
Если файл будет 20 мб, - выделит 20 мб и распакует его. Я незнаю что будет если файл будет размером в 500 мб, по идее тоже самое.
Мне вот например нужно, прочитать всего лишь кусочек в 10 кб из запакованного файла (одного из) - заголовок короче говоря.. Нет смысла распаковывать весь файл из архива.
Вот я и думаю толи свой написать, то ли не надо...
← →
Rouse_ © (2016-05-22 18:05) [6]
> K-1000 © (22.05.16 15:53) [4]
> Почему?
Ну те которые я смотрел не поддерживали ZIP64 спецификацию, шифрование, к памяти и скорости работы были претензии. Я свой писал чтобы учесть все эти недостатки и использую уже шестой год (да и не только я, уже достаточно много софта в том числе и коммерческого, написано с использованием моих классов).
← →
sniknik © (2016-05-23 10:41) [7]> у зипа есть такое поле в структуре каждого запакованного файла в TZipHeader.
так Zlib вроде не zip, не полноценный zip, а как бы "внутренняя кухня" отвечает только за данные, типа архивирование "на лету" мимо проходящих потоков (потому и используется в http).
т.е.function TStream.GetSize: Int64;
var
Pos: Int64;
begin
Pos := Seek(0, soCurrent);
Result := Seek(0, soEnd);
Seek(Pos, soBeginning);
end;
написан правильно, единственно возможным способом... а если "обертка сверху" для zip-а заимела заголовки с размером, то и метод нужно было бы перекрыть.
в общем проблема не тут
> Zlib - TZDecompressionStream.Seek написан криво
использую его в "чистом" виде, и вроде все правильно.
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2017.07.16;
Скачать: [xml.tar.bz2];
Память: 0.47 MB
Время: 0.001 c