-
Или не используйте метод 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);
-
Да, а вы спросите, как же задается размер буфера в этом методе TZipFile.Read(const FileName: string; out Bytes: TBytes);
он достается из заранее сохраненного поля UncompressedSize - у зипа есть такое поле в структуре каждого запакованного файла в TZipHeader.
-
А вот например метод
TZipFile.Extract(const FileName: string; const Path: string; CreateSubDirs: Boolean);
Тут ребята исхитрились, и сделали так:
LOutStream.CopyFrom(LInStream, FFiles[Index].UncompressedSize);
Тот самое поле из структуры.. При этом FFiles находится в привате.
-
-
-
> K-1000 © (22.05.16 15:53) [4]
Тут имеется в виду TZipFile из zip. Не фонтан он потому, что целиком выделяет буфер для всего распакованного файла. Если файл будет 20 мб, - выделит 20 мб и распакует его. Я незнаю что будет если файл будет размером в 500 мб, по идее тоже самое.
Мне вот например нужно, прочитать всего лишь кусочек в 10 кб из запакованного файла (одного из) - заголовок короче говоря.. Нет смысла распаковывать весь файл из архива.
Вот я и думаю толи свой написать, то ли не надо...
-
> K-1000 © (22.05.16 15:53) [4] > Почему?
Ну те которые я смотрел не поддерживали ZIP64 спецификацию, шифрование, к памяти и скорости работы были претензии. Я свой писал чтобы учесть все эти недостатки и использую уже шестой год (да и не только я, уже достаточно много софта в том числе и коммерческого, написано с использованием моих классов).
-
> у зипа есть такое поле в структуре каждого запакованного файла в 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 написан криво использую его в "чистом" виде, и вроде все правильно.
|