MARC import: part 3 of large file support
[koha.git] / C4 / UploadedFile.pm
1 package C4::UploadedFile;
2
3 # Copyright (C) 2007 LibLime
4 # Galen Charlton <galen.charlton@liblime.com>
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
19 # Suite 330, Boston, MA  02111-1307 USA
20
21 use strict;
22 use C4::Context;
23 use C4::Auth qw/get_session/;
24 use IO::File;
25
26 use vars qw($VERSION);
27
28 # set the version for version checking
29 $VERSION = 3.00;
30
31 =head1 NAME
32
33 C4::UploadedFile - manage files uploaded by the user
34 for later processing.
35
36 =head1 SYNOPSIS
37
38 =over 4
39
40 # create and store data
41 my $uploaded_file = C4::UploadedFile->new($sessionID);
42 my $fileID = $uploaded_file->id();
43 $uploaded_file->name('c:\temp\file.mrc');
44 $uploaded_file->max_size(1024);
45 while ($have_more_data) {
46     $uploaded_file->stash($data, $bytes_read);
47 }
48 $uploaded_file->done();
49
50 # check status of current file upload
51 my $progress = C4::UploadedFile->upload_progress($sessionID);
52
53 # get file handle for reading uploaded file
54 my $uploaded_file = C4::UploadedFile->fetch($fileID);
55 my $fh = $uploaded_file->fh();
56
57 =back
58
59 Stores files uploaded by the user from their web browser.  The
60 uploaded files are temporary and at present are not guaranteed
61 to survive beyond the life of the user's session.
62
63 This module allows for tracking the progress of the file
64 currently being uploaded.
65
66 TODO: implement secure persistant storage of uploaded files.
67
68 =cut
69
70 =head1 METHODS
71
72 =cut
73
74 =head2 new
75
76 =over 4
77
78 my $uploaded_file = C4::UploadedFile->new($sessionID);
79
80 =back
81
82 Creates a new object to represent the uploaded file.  Requires
83 the current session ID.
84
85 =cut
86
87 sub new {
88     my $class = shift;
89     my $sessionID = shift;
90
91     my $self = {};
92
93     $self->{'sessionID'} = $sessionID;
94     $self->{'fileID'} = Digest::MD5::md5_hex(Digest::MD5::md5_hex(time().{}.rand().{}.$$));
95     # FIXME - make staging area configurable
96     my $TEMPROOT = "/tmp";
97     my $OUTPUTDIR = "$TEMPROOT/$sessionID";
98     mkdir $OUTPUTDIR;
99     my $tmp_file_name = "$OUTPUTDIR/$self->{'fileID'}";
100     my $fh = new IO::File $tmp_file_name, "w";
101     unless (defined $fh) {
102         return undef;
103     }
104     $fh->binmode(); # Windows compatibility
105     $self->{'fh'} = $fh;
106     $self->{'tmp_file_name'} = $tmp_file_name;
107     my $session = get_session($sessionID);
108     $session->param("$self->{'fileID'}.uploaded_tmpfile", $tmp_file_name);
109     $session->param('current_upload', $self->{'fileID'});
110     $session->flush();
111     $self->{'session'} = $session;
112     $self->{'name'} = '';
113     $self->{'max_size'} = 0;
114     $self->{'progress'} = 0;
115
116     bless $self, $class;
117
118     return $self;
119
120 }
121
122 =head2 id
123
124 =over 4
125
126 my $fileID = $uploaded_file->id();
127
128 =back
129
130 =cut
131
132 sub id {
133     my $self = shift;
134     return $self->{'fileID'};
135 }
136
137 =head2 name
138
139 =over 4
140
141 my $name = $uploaded_file->name();
142 $uploaded_file->name($name);
143
144 =back
145
146 Accessor method for the name by which the file is to be known.
147
148 =cut
149
150 sub name {
151     my $self = shift;
152     if (@_) {
153         $self->{'name'} = shift;
154         $self->{'session'}->param("$self->{'fileID'}.uploaded_filename", $self->{'name'});
155         $self->{'session'}->flush();
156     } else {
157         return $self->{'name'};
158     }
159 }
160
161 =head2 max_size
162
163 =over 4
164
165 my $max_size = $uploaded_file->max_size();
166 $uploaded_file->max_size($max_size);
167
168 =back
169
170 Accessor method for the maximum size of the uploaded file.
171
172 =cut
173
174 sub max_size {
175     my $self = shift;
176     @_ ? $self->{'max_size'} = shift : $self->{'max_size'};
177 }
178
179 =head2 stash
180
181 =over 4
182
183 $uploaded_file->stash($dataref, $bytes_read);
184
185 =back
186
187 Write C<$dataref> to the temporary file.  C<$bytes_read> represents
188 the number of bytes (out of C<$max_size>) transmitted so far.
189
190 =cut
191
192 sub stash {
193     my $self = shift;
194     my $dataref = shift;
195     my $bytes_read = shift;
196
197     my $fh = $self->{'fh'};
198     print $fh $$dataref;
199
200     my $percentage = int(($bytes_read / $self->{'max_size'}) * 100);
201     if ($percentage > $self->{'progress'}) {
202         $self->{'progress'} = $percentage;
203         $self->{'session'}->param("$self->{'fileID'}.uploadprogress", $self->{'progress'});
204         $self->{'session'}->flush();
205     }
206 }
207
208 =head2 done
209
210 =over 4
211
212 $uploaded_file->done();
213
214 =back
215
216 Indicates that all of the bytes have been uploaded.
217
218 =cut
219
220 sub done {
221     my $self = shift;
222     $self->{'session'}->param("$self->{'fileID'}.uploadprogress", 'done');
223     $self->{'session'}->flush();
224     $self->{'fh'}->close();
225 }
226
227 =head2 upload_progress
228
229 =over 4
230
231 my $upload_progress = C4::UploadFile->upload_progress($sessionID);
232
233 =back
234
235 Returns (as an integer from 0 to 100) the percentage
236 progress of the current file upload.
237
238 =cut
239
240 sub upload_progress {
241     my ($class, $sessionID) = shift;
242
243     my $session = get_session($sessionID);
244
245     my $fileID = $session->param('current_upload');
246
247     my $reported_progress = 0;
248     if (defined $fileID and $fileID ne "") {
249         my $progress = $session->param("$fileID.uploadprogress");
250         if (defined $progress) {
251             if ($progress eq "done") {
252                 $reported_progress = 100;
253             } else {
254                 $reported_progress = $progress;
255             }
256         }
257     }
258     return $reported_progress;
259 }
260
261 =head2 fetch
262
263 =over 4
264
265     my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID);
266
267 =back
268
269 Retrieves an uploaded file object from the current session.
270
271 =cut
272
273 sub fetch {
274     my $class = shift;
275     my $sessionID = shift;
276     my $fileID = shift;
277
278     my $self = {};
279
280     $self->{'sessionID'} = $sessionID;
281     $self->{'fileID'} = $fileID;
282     my $session = get_session($sessionID);
283     $self->{'session'} = $session;
284     $self->{'tmp_file_name'} = $session->param("$self->{'fileID'}.uploaded_tmpfile");
285     $self->{'name'} = $session->param("$self->{'fileID'}.uploaded_filename");
286     my $fh = new IO::File $self->{'tmp_file_name'}, "r";
287     $self->{'fh'} = $fh;
288
289     bless $self, $class;
290
291     return $self;
292 }
293
294 =head2 fh
295
296 =over
297
298 my $fh = $uploaded_file->fh();
299
300 =back
301
302 Returns an IO::File handle to read the uploaded file.
303
304 =cut
305
306 sub fh {
307     my $self = shift;
308     return $self->{'fh'};
309 }
310
311 =head1 AUTHOR
312
313 Koha Development Team <info@koha.org>
314
315 Galen Charlton <galen.charlton@liblime.com>
316
317 =cut