AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) FileSplitter: Probleme mit großen Dateien

FileSplitter: Probleme mit großen Dateien

Ein Thema von Luckie · begonnen am 25. Mär 2008 · letzter Beitrag vom 29. Mär 2008
Antwort Antwort
Seite 1 von 4  1 23     Letzte » 
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#1

FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 14:52
Habe folgende Mail bezüglich großer Dateien und meines FileSplitters bekommen:
Zitat:
FileSplitter 5.7.0.3 (andere Versionen habe ich nicht getestet) versagt,
wenn die gesplitteten Dateien größer als 2047 MB sein sollen (getestet
unter XP SP2 mit NTFS). Ich wollte eine 6664,95 MB große Datei splitten
und erhielt dabei folgende Effekte:

Bei Angabe der Dateigröße zwischen 2048 und 4095 MB erscheint eine
10-stellige Anzahl Teile, und beim Versuch der Ausführung beendet sich
das Programm mit einem Runtime error.

Bei Angabe von 4096 MB als Dateigröße erhält man 0 Dateien und eine
Ausführung ist nicht möglich.

Bei Angabe von 4097 MB und mehr subtrahiert das Programm 4096 MB von der
Eingabe. Bei 4097 MB erhalte ich in meinem Beispiel also 6665 Teile zu 1 MB.

Bei Angabe von 2 bzw. 3 bei der Anzahl Dateien wird die korrekte
Dateigröße angezeigt und das Programm auch ausgeführt. Man erhält dann
allerdings 2 bzw 3 Dateien zu je 1 MB (und die Batch-Datei zum
Zusammenfügen).
Das wundert mich etwas, da ich eigentlich überall mit Int64 arbeite, wenn es um die Dateigrößen etc. geht. Hier mal die relevanten Codeauschnitte:

Delphi-Quellcode:
function SplitFile(Filename, DestFolder: string; SplitSize, CntParts: Int64): Integer;

  function GetClusterSize(Drive: Char): Cardinal;
  var
    SectorPerCluster: Cardinal;
    BytesPerSector : Cardinal;
    NumberOfFreeClusters: Cardinal;
    TotalNumberOfClusters: Cardinal;
  begin
    GetDiskFreeSpace(PChar(Drive + ':\'), SectorPerCluster, BytesPerSector, NumberOfFreeClusters,
      TotalNumberOfClusters);
    Result := SectorPerCluster * BytesPerSector;
  end;

var
  hFile : THandle;
  SizeOfFile : Int64;
  hPart : THandle;
  Loop : Cardinal;
  Partname : string;
  BlockSize : Cardinal;
  MemBuffer : array of Byte;
  minlen : Int64;
  BytesToRead, BytesRead, BytesWritten: Integer;
  OverallBytesRead : Int64;
  ProgressCurrent, ProgressOld: Int64;
begin
  TickStart := GetTickCount;

  BlockSize := -(-GetClusterSize(FileName[1]) and -GetClusterSize(DestFolder[1]) and -1048576);
  SetLength(MemBuffer, BlockSize - 1);
  bRunning := 1;
  OverallBytesRead := 0;
  SizeOfFile := GetFileSize(PChar(Filename));
  hFile := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL
    or FILE_FLAG_SEQUENTIAL_SCAN or FILE_FLAG_WRITE_THROUGH, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    for Loop := 1 to CntParts do
    begin
      // Reset variables
      ProgressOld := 0;
      BytesToRead := SplitSize;
      // build filename of the parts
      Partname := DestFolder + '\' + ExtractFilename(Filename) + Format('.%3.3d', [Loop]);
      if FileExists(Partname) then
        DeleteFile(PChar(Partname));
      hPart := CreateFile(PChar(Partname), GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL
        or FILE_FLAG_SEQUENTIAL_SCAN or FILE_FLAG_WRITE_THROUGH, 0);
      if hPart <> INVALID_HANDLE_VALUE then
      begin
        repeat
          minlen := Min(length(MemBuffer), BytesToRead);
          BytesRead := FileRead(hFile, MemBuffer[0], minLen);
          if BytesRead > -1 then
          begin
            BytesWritten := FileWrite(hPart, MemBuffer[0], BytesRead);
            Dec(BytesToRead, length(MemBuffer));
            // progress stuff ////////////////////////////////////////////////////
            OverallBytesRead := OverallBytesRead + BytesWritten;
            ProgressCurrent := (OverallBytesRead * 100) div SizeOfFile;
            if ProgressCurrent <> ProgressOld then
            begin
              ProgressOld := ProgressCurrent;
            end;
            SendMessage(hApp, FSM_PROGRESS, ProgressCurrent, Integer(PChar(Partname)));
          end
          else
          begin
            MessageBox(hApp, PChar(SysErrorMessage(GetLastError)), PChar(APPNAME), 0);
            Break;
          end;
          //////////////////////////////////////////////////////////////////////
        until (BytesToRead <= 0) or (bRunning = 0);
      end;
      FileClose(hPart);
      if bRunning = 0 then
        Break;
    end;
    FileClose(hFile);
  end;
  SendMessage(hApp, FSM_FINISH, GetTickCount - TickStart, GetLastError());
  result := GetLastError();
end;
Delphi-Quellcode:
function CalcCntParts(const Filename: string; Size: Int64): Cardinal;
var
  FileSize : Int64; // >4GB
begin
  result := 0;
  if Size > 0 then
  begin
    FileSize := GetFileSize(PChar(Filename));
    if (FileSize > 0) and (FileSize div Size < High(Integer)) then
      result := (FileSize - 1) div Integer(Size) + 1;
  end;
end;

function CalcFileSize(const Filename: string; CntParts: Cardinal): Int64;
var
  FileSize : Int64;
begin
  Result := 0;
  FileSize := GetFileSize(PChar(Filename));
  if (FileSize > 0) and (CntParts <> 0) then
  begin
    Result := (FileSize div CntParts) + 1;
  end;
end;
Delphi-Quellcode:
////////////////////////////////////////////////////////////////////////////////
// Procedure : FileRead
// Comment : Reads the given amount of bytes into a buffer
function FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer;
begin
  if not ReadFile(THandle(Handle), Buffer, Count, LongWord(Result), nil) then
    Result := -1;
end;

////////////////////////////////////////////////////////////////////////////////
// Procedure : FileWrite
// Comment : Writes the buffer into the file
function FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer;
begin
  if not WriteFile(THandle(Handle), Buffer, Count, LongWord(Result), nil) then
    Result := -1;
end;
Ich weiß nicht, wo ich den Fehlergemacht haben soll.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
MrKnogge

Registriert seit: 9. Jun 2003
Ort: Pforzheim
2.458 Beiträge
 
Delphi 2007 Professional
 
#2

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 14:57
Moin Michael,

hast du mal eine dummy-Datei in der erwähnten Größe erstellt und deinen Filesplitter damit durch den debugger gejagt ?
Christian Bootz
Einstein ist tot, Newton ist tot,
und mir ist auch schon ganz schlecht...
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#3

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 15:47
Das ist das Problem. Ich habe keine Partition mehr übrig mit genügend Platz.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#4

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 15:54
Ich verstehe nicht, weshalb der Parameter CntParts überhaupt übergeben wird.
Angenommen, ich möchte eine Datei mit 5 GB in 650 MB grosse Stücke unterteilen.
Dann gehe ich doch so vor (Pseudocode):
Delphi-Quellcode:
Datei öffnen
while not eof do
begin
  ErzeugeNeueZieldatei;
  
  readsize := 0
  do
    LesePuffer; // 8 kB lesen
    SchreibeZieldatei; // bis zu 8 kB schreiben
    if readBytes <> Buffersize then break;
    readsize := readsize + readBytes;
  until readsize >= 650 MB;
  SchlieseZielDatei;
end;
Auf diese Art braucht man keine For-Schleife und entgeht den ganzen Problemen mit Int64 Werten.
Kleiner Nachteil meiner Lösung: die Grösse der Zieldatei muss ein Vielfaches der Puffergrösse sein.
Mit etwas Zusatzaufwand kann man das aber auch lösen.
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#5

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 16:09
Zitat von shmia:
Auf diese Art braucht man keine For-Schleife und entgeht den ganzen Problemen mit Int64 Werten.
Ich glaube, bei der for-Schleife dürfte es keine Problme mit dem Wertebereich geben. Wer will eine Datei schon in 2^32 Teile teilen.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#6

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 16:27
Zitat von Luckie:
Zitat von shmia:
Auf diese Art braucht man keine For-Schleife und entgeht den ganzen Problemen mit Int64 Werten.
Ich glaube, bei der for-Schleife dürfte es keine Problme mit dem Wertebereich geben. Wer will eine Datei schon in 2^32 Teile teilen.
Ich würde trotzdem keine for-schleife verwenden; der Parameter ist doch überflüssig.
Wir kennen die Dateigrösse und die Grösse der Teildateien. Bringt man jetzt noch die Anzahl der Teildateien ins Spiel,
ist das wie eine überbestimmte Gleichung.

SizeOfFile := GetFileSize(PChar(Filename)); Bist du sicher, dass du die richtige Funktion verwendest ?
Unter Delphi 5 sieht der Prototyp aus Windows.pas so aus:
function GetFileSize(hFile: THandle; lpFileSizeHigh: Pointer): DWORD; stdcall; Bist du sicher, dass deine Funktion korrekt arbeitet (also wirklich int64 liefert) ?
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#7

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 16:47
Sieht bei mir so aus:
Delphi-Quellcode:
function GetFileSize(Filename: String): Int64;
var
  fFile : THandle;
  wfd : TWIN32FINDDATA;
begin
  result := -1;
  if not FileExists(Filename) then
    exit;
  fFile := FindFirstfile(pchar(Filename), wfd);
  if fFile = INVALID_HANDLE_VALUE then
    exit;
  result := (wfd.nFileSizeHigh * (Int64(MAXDWORD) + 1)) + wfd.nFileSizeLow;
  windows.FindClose(fFile);
end;
Das mit der for-Schleife wäre zu überlegen. Wenn Teile angegeben werden, müsste man eben erst die Größe der Teilstücke ausrechnen. Ich werde mir das ml überlegen.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#8

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 17:14
Zitat von Luckie:
Sieht bei mir so aus:
Delphi-Quellcode:
function GetFileSize(Filename: String): Int64;
var
  fFile : THandle;
  wfd : TWIN32FINDDATA;
begin
  result := -1;
  if not FileExists(Filename) then
    exit;
  fFile := FindFirstfile(pchar(Filename), wfd);
  if fFile = INVALID_HANDLE_VALUE then
    exit;
  result := (wfd.nFileSizeHigh * (Int64(MAXDWORD) + 1)) + wfd.nFileSizeLow;
  windows.FindClose(fFile);
end;
Au, wo hast du denn diese Funktion her ?
Das mit dem Multiplizieren ((wfd.nFileSizeHigh * (Int64(MAXDWORD) + 1)) löst bei mir "schlechte Gefühle" aus.
Ich glaube die VCL bzw. der Compiler kann gar keine Multiplikationen der Bauart
64bit * 32Bit = 64bit
ausführen.
Delphi-Quellcode:
function FileGetSize(const FileName: string): Int64; // kopiert aus der JCL
var
  SearchRec: TSearchRec;
  OldMode: Cardinal;
  Size: TULargeInteger;
begin
  Result := -1;
  OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  try
    if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
    begin
      Size.LowPart := SearchRec.FindData.nFileSizeLow;
      Size.HighPart := SearchRec.FindData.nFileSizeHigh;
      Result := Size.QuadPart;
      SysUtils.FindClose(SearchRec);
    end;
  finally
    SetErrorMode(OldMode);
  end;
end;
Andreas
  Mit Zitat antworten Zitat
marabu

Registriert seit: 6. Apr 2005
10.109 Beiträge
 
#9

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 19:18
Hallo Andreas,

Zitat von shmia:
... Ich glaube die VCL bzw. der Compiler kann gar keine Multiplikationen der Bauart 64bit * 32Bit = 64bit ausführen. ...
du meinst den Compiler - und deiner kann sowas. Die Redefinition eines QuadWord anstatt einer Multiplikation ist aber der bessere Weg.

Freundliche Grüße
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#10

Re: FileSplitter: Probleme mit großen Dateien

  Alt 25. Mär 2008, 22:05
So, ich habe mal die vorgeschlagenen Änderungen gemacht. Anbei die aktuelle Exe.

Hätte jemand von euch genug Festplattenkapazität, um das bitte mal mit Dateien größer 6,6 GB und Teilstücken größer 2048 MB zu testen, eben wie inder Fehlermeldung im ersten Posting? Wäre sehr nett. Allerdings glaube ich nicht, dass die Änderungen etwas mit dem Fehler zu tun haben.

Mir fällt gerade ein, kann es sein das er versucht hat die Teilstücke auf einer FAT32 Partition zu erstellen? Die kann doch nur Dateien bis 2GB verwalten oder?
Angehängte Dateien
Dateityp: exe filesplitter_193.exe (23,0 KB, 9x aufgerufen)
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:19 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz