Error in file upload using perl "read() on unopened filehandle" - perl

I am facing read the file error while i am uploading a file using perl like this
fileparse_set_fstype('MSWin32');
my ($OriginalName,$OriginalPath) = fileparse( $CgiRef->{'filename'} );
my $LocalName = $_ . $OriginalName;
open(FILE, ">$config->{'BASE_PATH'}/files/$LocalName")
or die "Could not open file:$!";
my $Req = new CGI;
while (read($Req->param('filename'), my $Buffer, 1024))
{
print FILE $Buffer;
}
close(FILE)
And There is no problem in accesing $CgiRef->{'$filename'} or any refernce variables.
please let me know where is the actual problem while uploading.
now it shows the error
read() on unopened filehandle

You're trying to read from the wrong place. In CGI-land, use $cgi->upload('varname') to get a filehandle on the object you're trying to receive.
This modified version of your snippet should work:
fileparse_set_fstype('MSWin32');
my ($OriginalName,$OriginalPath) = fileparse( $CgiRef->{'filename'} );
my $LocalName = $_ . $OriginalName;
open(FILE, ">", "$config->{'BASE_PATH'}/files/$LocalName")
or die "Could not open file:$!";
my $Req = CGI->new();
# Get the filehandle for the upload content
my $Req_file = $Req->upload('filename');
# Save to FILE
while (<$Req_file>) {
print FILE;
}
close(FILE);
Please note, always use the 3 param version of open. It's cleaner, safer, and clearer. See Modern Perl for an explanation.
A full example of the whole process from HTML form to CGI processing can be found here.

Related

Writing to a file inside if statement not working in Perl

I've looked around here a bit and found similar questions but not exactly. If there is one, I apologize and please point me to it.
I have the following code. I'm trying to create a csv file of simply an ID pulled from a filename and the filename itself. This is the ENTIRE script.
use strict;
use warnings;
use File::Find;
find( \&findAllFiles, '.');
exit;
sub findAllFiles {
my #fp1;
my #fp2;
my $patId;
my $filename;
my $testvar = "hello again";
$filename = $File::Find::name;
if ($filename =~ /\.pdf$/) {
open (my $fh, '>', 'filenames.csv') or die "Failed to open - $!\n";
print $fh "starting...$testvar\n" or die "Failed to print to file - $!\n";
#fp1 = split('/', $filename);
#fp2 = split('_', $fp1[-1]);
$patId = $fp2[-1];
$patId =~ s/\.pdf$//;
print "Adding $patId, file = $filename\n";
print $fh "$patId,$filename\n" or die "File print error: $!";
close $fh or warn "close failed! - $!";
}
return;
}
The line that prints to the screen, prints perfectly.
If I take the file open/close and the first print statement out of the if block, it prints that line into the file, but not the data inside the block.
I've tried every combo I can think of and it doesn't work. I've alternated between '>' and '>>' since it clearly needs the append since it's looping over filenames, but neither works inside the if block.
Even this code above doesn't throw the die errors! It just ignores those lines! I'm figuring there's something obvious I'm missing.
Quoting File::Find::find's documentation:
Additionally, for each directory found, it will chdir() into that directory
It means that when you open inside findAllFiles, you are potentially opening a file filenames.csv inside a subdirectory of your initial directory. You can run something like find . -name filenames.csv from your terminal, and you'll see plenty of filenames.csv. You can change this behavior by passing no_chdir option to find:
find( { wanted => \&findAllFiles, no_chdir => 1}, '.');
(and additionally changing > for >> in your open)
However, personally, I'd avoid repeatedly opening and closing filenames.csv when you could open it just once before calling find. If you don't want to have your filehandle globally defined, you can always pass it as an argument to findAllFiles:
{
open my $fh, '>', 'filenames.csv' or die "Failed to open 'filenames.csv': $!";
find(sub { findAllFiles($fh) }, '.')
}
sub findAllFiles {
my ($fh) = #_;
...
filenames.csv will be created in the directory where the pdf is found, since find() changes directories as it searches. If that's not what you want, use an absolute path to open it (or open it before calling find, which seems like a better idea).

Cannot decode! Invalid Base58 Character(s)!

I am trying to run
base58perl.pl
in my terminal using the following command:
perl base58perl.pl
but I get the following error:
Cannot decode! Invalid Base58 Character(s)!
Here's the code:
my $fileSrc = 'base58.txt';
open my $fhSrc, $fileSrc or die "Could not open $fileSrc: $!";
my $fileDest = 'hex.txt';
open( my $fhDest, '>>', $fileDest) or die "Could not open file $fileDest: $!";
while ( my $base58_encoded_address = <$fhSrc >) {
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
}
close $fhSrc;
close $fhDest;
The content of base58.txt is a list of BTC address in base58 form.
I also have tried
chmod a+x base58perl.pl
perl base58perl.pl
base58.txt contents:
1E5PBfSaFawBy1RjBHkS6FDtCwXkYSsVTo
1DCgptTS2uY2occbVdW1qcVT72T75RXbyg
1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb
I still get the same error.
That error message comes from the unbase58 function in the code you have linked.
die "Cannot Decode! Invalid Base58 Character(s)!\n" unless $bitcoin_address =~ /^[1-9A-HJ-NP-Za-km-z]*$/;
That line checks if the input contains only characters of the character group [1-9A-HJ-NP-Za-km-z]. Since your input does, it must dislike something else.
My guess is that it disliked the newline characters at the end of your lines. You need to chomp them off before passing the value to decodebase58tohex.
while( my $base58_encoded_address = <$fhSrc>) {
chomp $base58_encoded_address;
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
}
You probably need to remove whitespace. You appear to be passing only chunks of the string to the decode function at a time, which could also be a problem. Read the whole file into a var, remove any whitespace, then decode.
my $base58_encoded_address = do { local $/; <$fhSrc> };
$base58_encoded_address =~ s/\s+//g;
my $binary_address = decodebase58tohex($base58_encoded_address);
say $fhDest $binary_address;
my $fileSrc = 'base58.txt';
open my $fhSrc, $fileSrc or die "Could not open $fileSrc: $!";
my $fileDest = 'hex.txt';
open( my $fhDest, '>>', $fileDest) or die "Could not open file $fileDest: $!";
my #tmp = <$fhSrc>;
chomp #tmp;
for my $line (#tmp) {
print "decoding '$line'\n";
my $binary_address = decodebase58tohex($line);
say $fhDest $binary_address;
}
close $fhSrc;
close $fhDest;
As someone else mentioned I think your dealing with whitespaces.
chomp will take care of that for you.
The next thing to do is print the string you are trying to decode in quotes which will confirm your only decoding what you want to.
The script is now working properly, the problem was the base58.txt the file was created using notepad. I created a new file using a different text editor.

How to upload multiple files in perl?

I need to upload multiple files using perl cgi.
i used form type as
enctype="multipart/form-data
and also set
multiple='multiple' in input type file.
just need to know what should we do write at server side ?
Can anybody tell me how to upload multiple files using perl?
The following piece of code is enough and upload files present in the params to /storage location:
use CGI;
my $cgi = new CGI;
my #files = $cgi->param('multi_files[]');
my #io_handles=$cgi->upload('multi_files[]');
foreach my $upload(#files){
print "Filename: $upload<br>";
my $file_temp_path = "/storage";
my $upload_file = shift #io_handles;
open (UPLOADFILE,">$file_temp_path/$upload") or print "File Open Error";
binmode UPLOADFILE;
while (<$upload_file>) {
print UPLOADFILE;
}
}
print "Files Upload done";
On the server side, you first retrive the file file handle like this:
use CGI;
my $q = CGI->new();
my $myfh = $q->upload('field_name');
Now you have a filehandle to the temporary storage whither the file was uploaded.
The uploaded file anme can be had using the param() method.
$filename = $q->param('field_name');
and the temporary file can be directly accessed via:
$filename = $query->param('uploaded_file');
$tmpfilename = $query->tmpFileName($filename);
I highly recommend giving the CGI.pm docs a good solid read, a couple of times. While not trivial, it's all rather straightforward.
Something like this should handle multiple files upload:
my #fhs = $Cgi->upload('files');
foreach my $fh (#fhs) {
if (defined $fh) {
my $type = $Cgi->uploadInfo($fh)->{'Content-Type'};
next unless ($type eq "image/jpeg");
my $io_handle = $fh->handle;
open (OUTFILE,'>>','/var/directory/'.$fh);
while (my $bytesread = $io_handle->read(my $buffer, 1024)) {
print OUTFILE $buffer;
}
close (OUTFILE);
}
}
Ofc 'files' is the name of the file upload form.

PERL CGI multiple content types. How To Download File AND view contents.

A page that prints out a file (on the server) contents and provides a direct download link.
Download File HERE
Start contents of file:
line 1
line 2
line 3
...
I am not sure of the best way and the right header that will allow a download link and HTML text. This prints out blank
print $mycgi->header(
-cookie => $mycookie,
-Type => "application/x-download"
-'Content-Disposition'=>'attachment; filename="FileName"'
);
You can include a link to a script and pass the filename as a parameter. The link might look something like this:
http://url/to/script?action=download&file=foo
Below that, simply print the contents of the file:
#!/usr/bin/perl -T
use strict;
use warnings;
use CGI qw/escapeHTML/;
my $q = CGI->new;
print $q->header,
$q->start_html('foo'),
$q->a({ -href => 'http://url/to/script?action=download&file=foo' }, 'Click to download'),
"<pre>";
open my $fh, "<", "/path/to/file" or die $!;
print escapeHTML($_) while <$fh>;
close $fh;
print "</pre>", $q->end_html;
Note that you should use escapeHTML() to prevent the browser from rendering anything in the file as HTML (which the <pre> tag alone does not take care of).
When the script is called with the action parameter set to download, use the application/x-download content type as you did above:
my $q = CGI->new;
# Untaint parameters
my ($action) = ($q->param('action') =~ /^(\w+)$/g);
my ($file) = ($q->param('file') =~ /^([-.\w]+)$/g);
# Map file parameter to the actual file name on your filesystem.
# The user should never know the actual file name. There are many
# ways you could implement this.
???
if ($action eq "download") {
print $q->header(
-type => "application/x-download",
-'Content-Disposition' => 'attachment; filename="FileName"'
);
open my $fh, "<", $file or die "Failed to open `$file' for reading: $!";
print while <$fh>;
close $fh;
}
Note that you also need to print the contents of the file in the body of the response.

How do I process the response as a file without using the :content_file option?

Example code:
my $ua = LWP::UserAgent->new;
my $response = $ua->get('http://example.com/file.zip');
if ($response->is_success) {
# get the filehandle for $response->content
# and process the data
}
else { die $response->status_line }
I need to open the content as a file without prior saving it to the disk. How would you do this?
You can open a fake filehandle that points to a scalar. If the file argument is a scalar reference, Perl will treat the contents of the scalar as file data rather than a filename.
open my $fh, '<', $response->content_ref;
while( <$fh> ) {
# pretend it's a file
}
Not quite a file, but here is a relevant SO question: What is the easiest way in pure Perl to stream from another HTTP resource?