Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 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
8-1243163026
Ferreira_II
2009-05-24 15:03
2017.07.16
Конвертирование аудиофайлов


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


15-1464125403
Юрий
2016-05-25 00:30
2017.07.16
С днем рождения ! 25 мая 2016 среда


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


2-1442377244
vegarulez
2015-09-16 07:20
2017.07.16
Twebbrowser+IIHTMLDocument2 парсинг + innerhtml ?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский