Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2017.07.16;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.5 MB
Время: 0.005 c
15-1464125403
Юрий
2016-05-25 00:30
2017.07.16
С днем рождения ! 25 мая 2016 среда


2-1440613972
RZD
2015-08-26 21:32
2017.07.16
Крупный шрифт на D-7 на разных компах выглядит по разному!?


15-1463907824
Jeer
2016-05-22 12:03
2017.07.16
Питерцы - победители в чм по программированию.


15-1463888094
Кто б сомневался
2016-05-22 06:34
2017.07.16
Zlib - TZDecompressionStream.Seek написан криво


2-1441812254
pavelnk
2015-09-09 18:24
2017.07.16
Просветите по округлению