![]() |
FileSplitter: Probleme mit großen Dateien
Habe folgende Mail bezüglich großer Dateien und meines FileSplitters bekommen:
Zitat:
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:
Ich weiß nicht, wo ich den Fehlergemacht haben soll.
////////////////////////////////////////////////////////////////////////////////
// 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; |
Re: FileSplitter: Probleme mit großen Dateien
Moin Michael,
hast du mal eine dummy-Datei in der erwähnten Größe erstellt und deinen Filesplitter damit durch den debugger gejagt ? |
Re: FileSplitter: Probleme mit großen Dateien
Das ist das Problem. Ich habe keine Partition mehr übrig mit genügend Platz. ;)
|
Re: FileSplitter: Probleme mit großen Dateien
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:
Auf diese Art braucht man keine For-Schleife und entgeht den ganzen Problemen mit Int64 Werten.
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; 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. |
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
|
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
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.
Delphi-Quellcode:
Bist du sicher, dass du die richtige Funktion verwendest ?
SizeOfFile := GetFileSize(PChar(Filename));
Unter Delphi 5 sieht der Prototyp aus Windows.pas so aus:
Delphi-Quellcode:
Bist du sicher, dass deine Funktion korrekt arbeitet (also wirklich int64 liefert) ?
function GetFileSize(hFile: THandle; lpFileSizeHigh: Pointer): DWORD; stdcall;
|
Re: FileSplitter: Probleme mit großen Dateien
Sieht bei mir so aus:
Delphi-Quellcode:
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.
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; |
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
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; |
Re: FileSplitter: Probleme mit großen Dateien
Hallo Andreas,
Zitat:
Freundliche Grüße |
Re: FileSplitter: Probleme mit großen Dateien
Liste der Anhänge anzeigen (Anzahl: 1)
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? |
Re: FileSplitter: Probleme mit großen Dateien
Abend Luckie,
ich habe gerade eine 7GB große Testdatei erstellt und erhalte folgendes Phänomen: Ich wähle die Dateigröße 2000 MB und im Editfeld darunter erscheint "4", wenn ich aber 2500 MB einstelle möchte dein tool die Datei in 4294967293 Teile teilen. Und dies tritt ab 2048 MB auf. |
Re: FileSplitter: Probleme mit großen Dateien
Das sieht ganz nach einem Integer Überlauf aus. Aber wo? Das kann ja eigentlich nur hier passieren:
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; |
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
|
Re: FileSplitter: Probleme mit großen Dateien
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe es jetzt mal so geändert:
Delphi-Quellcode:
Neue Exe im Anhang.
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(Int64)) then result := (FileSize - 1) div Int64(Size) + 1; end; end; |
Re: FileSplitter: Probleme mit großen Dateien
Size ist doch schon int64, wieso noch ein Typecast :gruebel:
|
Re: FileSplitter: Probleme mit großen Dateien
Nun zeigt er bei 2500 MB schon mal 3 Teile an, teilt die Datei aber in lauter 1024 KB große Dateien :shock:
|
Re: FileSplitter: Probleme mit großen Dateien
Guten Abend ebenfalls,
Ich habe sehr viel freien Festplattenspeicher (wer braucht schon 500 GB um seine Files zu verwalten...) und hab ein ca 4,5 GB großes CD-Image mithilfe eines Editors (der aufgrund der Dateigröße selbst geschrieben wurde... :wink:) vervierfacht (copy & paste), sodass am ende eine 18 GB große Testdatei da war. Die hab ich nun versucht zu splitten (mit der neuen Version). Die Teile berechnet er nun richtig, aber am Ende Kommen nur 1 Mega-Files heraus. (Aua, das gibt eine Beule im Papierkorb :wink:) Ich weiß, ihr findet mit 18 GB übertreib ich es ein wenig, war aber so mfg St!nkeSchuh EDIT: Hab deine neue Version nicht berücksichtigt |
Re: FileSplitter: Probleme mit großen Dateien
@StInkeSchuh
Wie groß hast du die Teile festgelegt, Probleme treten wohl nur bei größer als 2047MB auf. |
Re: FileSplitter: Probleme mit großen Dateien
@MrKnogge:
Die Teile waren einmal 9 GB, dann 4,5 und zum Schluss 2 GB. Hat aber gefunzt, bis ich im Exploder (= heisst bei mir Explorer) 18.000 einen Megabyte große Dateien gefunden habe. Also die Berechnung ist korrekt, die Umsetzung leider noch nicht. :wink: |
Re: FileSplitter: Probleme mit großen Dateien
Mist. Dann muss ich noch mal weitersuchen.
|
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
|
Re: FileSplitter: Probleme mit großen Dateien
Nein, es erscheint alles korrekt, sowohl Größe als auch Teilstücke, deswegen war ich ja verwundert als ich mir mein C:\ angeschaut habe :spin2:
|
Re: FileSplitter: Probleme mit großen Dateien
Dann liegt der Hund in der Split-Funktion begraben. :gruebel:
|
Re: FileSplitter: Probleme mit großen Dateien
Kannst du den aktuellen Source mal anhängen, oder mir schicken ?
Dann jag ichs mal durch den debugger, ich glaub sonst sucht man sich da einen Wolf... |
Re: FileSplitter: Probleme mit großen Dateien
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für das Angebot. Sourcen im Anhang.
|
Re: FileSplitter: Probleme mit großen Dateien
Kein Problem, ich erhalte bei BytesToRead einen Überlauf. BytesToRead, BytesRead, BytesWritten sind noch Integer.
Mit Int64 funktioniert es, allerdings etwas langsam, rund 1-2 Sekunden pro Prozent. Könnte man das etwas beschleuigen, in dem man die Blöckgröße etwas vergrößert? Bei mir ist die knapp über 1MB. |
Re: FileSplitter: Probleme mit großen Dateien
Das hätte ich jetzt nicht gedacht, dass die Blockgröße einen Interger-Überlauf verursachen könnte. :shock:
Besten dank fürs Debuggen. :P |
Re: FileSplitter: Probleme mit großen Dateien
Liste der Anhänge anzeigen (Anzahl: 1)
Zu früh gefreut:
Zitat:
Aktuelle Version im Anhang. |
Re: FileSplitter: Probleme mit großen Dateien
Moin Luckie, der fehler liegt in deiner Funktion dlgfunc, diese übergibt nämlich bei 4096 MB für den Parameter Size den Wert 0 an CalcCntParts. Wo der fehler genau liegt, kann ich nicht sagen. (Habe nie viel in NonVCL gemacht)
|
Re: FileSplitter: Probleme mit großen Dateien
Hm. :gruebel:
Danke schon mal für den Hinweis. Mal sehen, ob ich da was finden kann. |
Re: FileSplitter: Probleme mit großen Dateien
Ich hab jetzt nochmal etwas nachgesehen, der Fehler kommt aus GetDlgItemInt (Zeile 519).
|
Re: FileSplitter: Probleme mit großen Dateien
Ja, so was habe ich schon vermutet. Bin blos noch nicht dazu gekommen mir das näher anzugucken. Aber die Größe der Zahl aus dem Edit dürfte den Wertebereich vin Integer aber nicht übersteigen.
|
Re: FileSplitter: Probleme mit großen Dateien
Ich habs gefunden :wall:
Hätte mir eigentlich auch eher auffallen müssen, gleiches hatte ich nämlich als ich mir die Dummy-Datei zum splitten erzeugt habe. Mach einfach aus:
Delphi-Quellcode:
dies hier:
SizeOfParts := GetDlgItemInt(hDlg, IDC_EDT_SIZE, Translated, False) * 1024 * 1024;
Delphi-Quellcode:
SizeOfParts := GetDlgItemInt(hDlg, IDC_EDT_SIZE, Translated, False) * 1024;
SizeOfParts := SizeOfParts * 1024; |
Re: FileSplitter: Probleme mit großen Dateien
Was passiert denn da im Debugger? Und wenn dann würde ich eventuell gleich
Delphi-Quellcode:
das rausmachen. Könntets du das bitte noch mal eben testen?
SizeOfParts := GetDlgItemInt(hDlg, IDC_EDT_SIZE, Translated, False);
SizeOfParts := SizeOfParts * 1024 * 1024; |
Re: FileSplitter: Probleme mit großen Dateien
Diese Variante geht auch. Warum Delphi ein problem dabei hat, verstehe ich auch nicht.
In diesem Fall steht in SizeOfParts die Größe der Parts wie sie im Editfeld steht. Ich vermute stark, dass Delphi ein Problem beim Multiplizieren von Integer-Konstanten hat. Folgendes funktioniert nämlich:
Delphi-Quellcode:
SizeOfParts := GetDlgItemInt(hDlg, IDC_EDT_SIZE, Translated, False) * round(1024.0) * round(1024.0);
|
Re: FileSplitter: Probleme mit großen Dateien
Moin moin,
Zitat:
Result liefert und auch alle anderen Multiplikatoren Delphi zu nichts anderem veranlassen, wird da auch weiter nichts berücksichtigt. Du kannst es austesten, in dem du direkt den maximalen LongWord Wert anstelle der Funktion GetDlgItemInt eingibst. Also:
Delphi-Quellcode:
Jetzt "bemerkt" der Compiler den Überlauf und meckert das auch an.
SizeOfParts := 1024*1024*4294967295;
Folgender Code weißt Delphi an, mit 64 Bit zu rechen,
Delphi-Quellcode:
und voilà es lässt sich kompilieren + das Ergebnis stimmt.
SizeOfParts := Int64(1024)*1024*4294967295;
Hier ist jetzt genau ein Wert mit bei, der vom Typ Int64 ist. Somit richtet der Compiler alles auf 64 Bit aus. Es müsste also theoretisch ausreichen, einen Faktor in Int64 zu casten. |
Re: FileSplitter: Probleme mit großen Dateien
Moin,
die Rechnung lautete zwar 4096 * 1024 * 1024 aber der Hintergrund bleibt ja der gleiche, da die beiden Konstanten im Bereich von 0 bis + ~4,2Mrd liegen nutzt Delphi einen vorzeichenlosen 32Bit Bereich. Ich war bisher immer der Meinung, dass der Compiler sich nach der "Ziel-Variable" richtet. |
Re: FileSplitter: Probleme mit großen Dateien
Zitat:
Dann besten Dank noch mal fürs testen, Debuggen und Erklären. :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:27 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz