2012-10-31 20 views
8

Delphi XE2/XE3 standart System.Zip birimi ile bir zip arşivinde bir dosyayı (= eski sil ve yeni ekle) değiştirmek istiyorum. Ancak değiştirme/silme yöntemleri yoktur. Herhangi bir kişinin tüm dosyaları ayıklayıp yeni bir arşive eklemesine gerek kalmadan nasıl elde edilebileceğine dair bir fikri var mı?Delphi XE2 TZipFile: bir dosyayı zip arşivinde değiştir

Bu kodu vardır, ama buna zaten mevcut olmadığını bir kez daha "document.txt" ekler:

var 
    ZipFile: TZipFile; 
    SS: TStringStream; 
const 
    ZipDocument = 'E:\document.zip'; 
begin 
    ZipFile := TZipFile.Create; //Zipfile: TZipFile 
    SS := TStringStream.Create('hello'); 
    try 
    if FileExists(ZipDocument) then 
     ZipFile.Open(ZipDocument, zmReadWrite) 
    else 
     ZipFile.Open(ZipDocument, zmWrite); 

    ZipFile.Add(SS, 'document.txt'); 

    ZipFile.Close; 
    finally 
    SS.Free; 
    ZipFile.Free; 
    end; 
end; 

Not: I (iş yaptı o) önce TPAbbrevia kullanılabilir, ancak İsterdim Delphi'nin Zip ünitesini şimdi kullanmak. Yani lütfen "başka bir kütüphane kullan" gibi bir şeye cevap vermeyin. Teşekkür ederim.

+1

. Yerleşik ZIP kitaplığı bu işlevselliği desteklemiyor. –

+0

Belki birisi onun yaptığı bir hack yazdı? – oxo

+0

Neden Abbrevia'yı kullanmıyorsunuz? Çok iyi olduğu söylendi. –

cevap

12

Abbrevia'yı öneriyorum çünkü önyargılıyım :), zaten biliyorsunuz ve herhangi bir korsanlık gerektirmiyor. Bu çözümün engellenmesi, burada beygir:

type 
    TZipFileHelper = class helper for TZipFile 
    procedure Delete(FileName: string); 
    end; 

{ TZipFileHelper } 

procedure TZipFileHelper.Delete(FileName: string); 
var 
    i, j: Integer; 
    StartOffset, EndOffset, Size: UInt32; 
    Header: TZipHeader; 
    Buf: TBytes; 
begin 
    i := IndexOf(FileName); 
    if i <> -1 then begin 
    // Find extents for existing file in the file stream 
    StartOffset := Self.FFiles[i].LocalHeaderOffset; 
    EndOffset := Self.FEndFileData; 
    for j := 0 to Self.FFiles.Count - 1 do begin 
     if (Self.FFiles[j].LocalHeaderOffset > StartOffset) and 
     (Self.FFiles[j].LocalHeaderOffset <= EndOffset) then 
     EndOffset := Self.FFiles[j].LocalHeaderOffset; 
    end; 
    Size := EndOffset - StartOffset; 
    // Update central directory header data 
    Self.FFiles.Delete(i); 
    for j := 0 to Self.FFiles.Count - 1 do begin 
     Header := Self.FFiles[j]; 
     if Header.LocalHeaderOffset > StartOffset then begin 
     Header.LocalHeaderOffset := Header.LocalHeaderOffset - Size; 
     Self.FFiles[j] := Header; 
     end; 
    end; 
    // Remove existing file stream 
    SetLength(Buf, Self.FEndFileData - EndOffset); 
    Self.FStream.Position := EndOffset; 
    if Length(Buf) > 0 then 
     Self.FStream.Read(Buf[0], Length(Buf)); 
    Self.FStream.Size := StartOffset; 
    if Length(Buf) > 0 then 
     Self.FStream.Write(Buf[0], Length(Buf)); 
    Self.FEndFileData := Self.FStream.Position; 
    end; 
end; 

Kullanımı: Kendi soru cevap var

ZipFile.Delete('document.txt'); 
ZipFile.Add(SS, 'document.txt'); 
+0

+1 Bu hack, eski dosyayı ZIP'den gerçekten kaldırır mı yoksa sadece dosya tablosundan mı yoksa çağrıldığından mı çıkarır? –

+0

Hayır, dosyayı fiziksel olarak kaldırmaz (zip dosyası büyür), ancak iyi bir başlangıçtır. Kodu genişletmeye ve eski dosyayı FStream'den silmeye ve üstbilgiyi (yeni dosya konumları) yeniden hesaplamaya çalışacağım. – oxo

+1

@David iyi yakalama; Uygulamaya daha yakından bakmalıydım. Dosya içeriğini silen daha eksiksiz bir hack ile güncellendi. –