Author: Jonas Bilinkevicius
What is the quickest way of merging loads of files together, and being able to pull
them out when needed in the application all files have unique names, I need to
merge the files as the application could create 10000+ and all them being in one
dirctory, well lets say windows does not handle it very well specially the fact
that they are all small file with the odd occasion of a 15mb file, so I need a
better way off managing it not interested in compression I want something that is
as quick or quicker than access an individual file.
Answer:
Solve 1:
If you do not need random access to the files in the larger file (in which case you
need an index, a kind of directory) you can simply concatenate the source files,
storing the file name and size for each file in front of the files data.
1
2 procedure ConCatFiles(const targetname: string; const Sourcenames: TStrings);
3 var
4 i: Integer;
5 target, source: TFileStream;
6 fsize: Longint;
7 begin
8 target := TFileStream.Create(targetname, fmCreate);
9 try
10 for i := 0 to Sourcenames.Count - 1 do
11 begin
12 source := TFileStream.Create(Sourcenames[i], fmOpenread or fmShareDenyNone);
13 try
14 fsize := Length(Sourcenames[i]);
15 target.write(fsize, Sizeof(fsize));
16 target.write(Sourcenames[i][1], fsize);
17 fsize := source.size;
18 target.write(fsize, Sizeof(fsize));
19 target.Copyfrom(source, 0);
20 finally
21 source.free;
22 end;
23 end;
24 finally
25 target.Free;
26 end;
27 end;
28
29 procedure UnmergeFiles(const sourcename: string);
30 var
31 i: Integer;
32 target, source: TFileStream;
33 fsize, sourcesize: Longint;
34 fname: string;
35 begin
36 source := TFileStream.Create(sourcename, fmOpenread or fmShareDenyNone);
37 try
38 sourcesize := source.size;
39 while source.position < sourcesize do
40 begin
41 source.read(fsize, Sizeof(fsize));
42 SetLength(fname, fsize);
43 source.read(fname[1], fsize);
44 target := TFileStream.Create(fname, fmCreate);
45 try
46 source.read(fsize, Sizeof(fsize));
47 target.Copyfrom(source, fsize);
48 finally
49 target.free;
50 end;
51 end;
52 finally
53 source.Free;
54 end;
55 end;
Untested! And of course you should think about how to handle pathes in this context.
Solve 2:
I've written a little example that doesn't consume too much memory. It concatenates
and compresses files into one destination file (CompressFiles) and can restore then
in a given location (DecompressFiles).
56 { ... }
57 implementation
58
59 {$R *.dfm}
60
61 uses
62 zLib;
63
64 procedure CompressFiles(Files: TStrings; const Filename: string);
65 var
66 infile, outfile, tmpFile: TFileStream;
67 compr: TCompressionStream;
68 i, l: Integer;
69 s: string;
70 begin
71 if Files.Count > 0 then
72 begin
73 outFile := TFileStream.Create(Filename, fmCreate);
74 try
75 {the number of files}
76 l := Files.Count;
77 outfile.write(l, SizeOf(l));
78 for i := 0 to Files.Count - 1 do
79 begin
80 infile := TFileStream.Create(Files[i], fmOpenRead);
81 try
82 {the original filename}
83 s := ExtractFilename(Files[i]);
84 l := Length(s);
85 outfile.write(l, SizeOf(l));
86 outfile.write(s[1], l);
87 {the original filesize}
88 l := infile.Size;
89 outfile.write(l, SizeOf(l));
90 {compress and store the file temporary}
91 tmpFile := TFileStream.Create('tmp', fmCreate);
92 compr := TCompressionStream.Create(clMax, tmpfile);
93 try
94 compr.CopyFrom(infile, l);
95 finally
96 compr.Free;
97 tmpFile.Free;
98 end;
99 {append the compressed file to the destination file}
100 tmpFile := TFileStream.Create('tmp', fmOpenRead);
101 try
102 outfile.CopyFrom(tmpFile, 0);
103 finally
104 tmpFile.Free;
105 end;
106 finally
107 infile.Free;
108 end;
109 end;
110 finally
111 outfile.Free;
112 end;
113 DeleteFile('tmp');
114 end;
115 end;
116
117 procedure DecompressFiles(const Filename, DestDirectory: string);
118 var
119 dest, s: string;
120 decompr: TDecompressionStream;
121 infile, outfile: TFilestream;
122 i, l, c: Integer;
123 begin
124 dest := IncludeTrailingPathDelimiter(DestDirectory);
125 infile := TFileStream.Create(Filename, fmOpenRead);
126 try
127 {number of files}
128 infile.read(c, SizeOf(c));
129 for i := 1 to c do
130 begin
131 {read filename}
132 infile.read(l, SizeOf(l));
133 SetLength(s, l);
134 infile.read(s[1], l);
135 {read filesize}
136 infile.read(l, SizeOf(l));
137 {decompress the files and store it}
138 s := dest + s; {include the path}
139 outfile := TFileStream.Create(s, fmCreate);
140 decompr := TDecompressionStream.Create(infile);
141 try
142 outfile.CopyFrom(decompr, l);
143 finally
144 outfile.Free;
145 decompr.Free;
146 end;
147 end;
148 finally
149 infile.Free;
150 end;
151 end;
|