可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Friends,
I have been scouring the web for a solution to displaying images to a web browser with Perl and have found nothing that works for me.
I've tried possible solutions such as:
How To Display an Image with Perl
Outputting Image Data
Return an Image From a Script
and none of it works for what I'm doing. I want to deny client access to the image (Or even simply place the image file out of the www root) and dish it out server-side.
Here is an example of what I'm doing:
In my main perl file:
...
my $query = CGI->new();
sub main {
### This grabs page content from a module, depending on the page name.
### The module returns the HTML.
my $html = get_page('page', 'session');
### Perform any special conditionals here before printing the header...
print $query->header(some cookie/session data here);
print $html
}
In one of the modules:
sub return_page_content {
return <<HTML;
<html>
<body>
<img src="GET IMAGE HERE..." />
</body>
</html>
HTML
}
I've thought about just creating a copy of the image in a temp directory location, but that seems like it would defeat the entire purpose of keeping the image out of client-side access.
The probable solutions do not generate the image. I'm not sure where to go from here, so I am hoping someone here has an idea. Thank you so much!
Please let me know if I need to provide additional information. I feel like this could be beneficial to a lot of people. I hope at least ;-)
回答1:
For a simple 'send the file to the browser solution', just send the browser the correct headers (to let the browser know what's coming), and then open your image and print the content to STDOUT.
select(STDOUT); $| = 1; #unbuffer STDOUT
print "Content-type: image/png\n\n";
open (IMAGE, '<', '/image_outside_webroot/image.png');
print <IMAGE>;
close IMAGE;
Once that is working, take a look at ImageMagick. There are all kinds of on-the-fly, fun image manipulations you can do (resizing, colorizing, etc.)
Your cgi script would contain code that looks something like this:
select(STDOUT); $| = 1; #unbuffer STDOUT
my $image = Image::Magick->new();
my $x = $image->Read(filename =>"/images_outside_web_root/image1.png");
#some manipulation of the image here
print "Content-type: image/png\n\n";
binmode STDOUT;
$x = $image->Write(.png:-');
You can read more about this on the site linked above.
Hope that helps.
回答2:
Well, it turns out the reason this wasn't working is because I completely forgot I do not operate with CGI out of the standard cgi_bin directory. Instead, I use an .htaccess file to tell the server how and when to interpret files from the root directory as cgi.
So, this is what I ended up using in my image dishing script:
imagedish.pm
use CGI;
my $cgi = new CGI;
open (IMAGE, 'logo.png');
my $size = -s "logo.png";
my $data;
read IMAGE, $data, $size;
close (IMAGE);
print $cgi->header(-type=>'image/png'), $data;
This did the trick, as it should have been doing from the beginning, but in my .htaccess file, I added:
<files imagedish.pm>
SetHandler cgi-script
</files>
And that did the trick (Well, that, as well as going into Terminal and running chmod +x imagedish.pm to make it executable)! Of course the next steps are additional security measures, but at least it's working now! :-)
The full solution:
mainfile:
!/usr/bin/perl
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use CGI::Session qw/-ip-match/;
use DBI;
use strict;
use warnings;
# Variables
my $query = CGI->new();
my %vars = $query->Vars();
sub main {
my $p1 = $vars{p1};
$p1 = 'Home' if (!$p1);
my $html = get_page();
#I use this method in case we have multiple sessions
#I've omitted how I acquire the session, as this is not part of the solution ;-)
print $query->header(-cookie=>[$query->cookie($vars{p1}=>$session->id)]);
print $html;
}
sub get_page {
return <<HTML;
<!DOCTYPE HTML>
<html>
<head>
<title>Image Disher</title>
<link rel="shortcut icon" href="images/favicon.ico" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<div class="addentry">
<div class="iaddentry">
<form name="client" action="" method="POST">
<div class="form-header" action="">
<center>
<img src="imagedish.pm" width="305" alt=""/><br>
</center>
<br>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
HTML
}
main();
In here, the img tag looks for the source "imagedish.pm", and once it finds it, the .htaccess file tells it to execute as a CGI script. At that point, it dishes out information appropriately, not like before.
Please note, this is not the most-secure way to do it, but it gets me going in the right direction.
回答3:
The links you found (except the first one) describe how to do it, I think you are just getting confused with the difference between delivering an image and delivering an html file with an img tag on it. Keep in mind that when your browser is parsing an html file and it encounters an img tag, it takes the src url in the tag and makes an additional get request for it.
Try capturing the raw output from a request for an image using curl, wireshark, etc. The result is what you want to try to create. It's just a matter of returning the content type http header, followed by the binary image data.
Have another look at this example, and get rid of the random_file sub, and replace this line:
my $image = random_file( IMAGE_DIRECTORY, '\\.(png|jpg|gif)$' );
with this:
my $image = "path to an image file accessible by www-user";
Hopefully once you that working and understand what it's doing, you'll know what you need to do next.
回答4:
Yet another way is to embed the image using the <img src="data:image/...,base64,...">
format.
This defeats browser caching and isn't great for very large images. But is useful if its easier to construct the image as part of the initial page load or you don't want the hassle of managing/serving them via a file system.
#!/user/bin/perl
use warnings; use strict;
use MIME::Base64 qw();
sub return_page_content {
my $image_type = shift;
my $image_data = shift;
my $image_base64 = MIME::Base64::encode($image_data);
$image_base64 =~ s{\n}{}g; # lose newlines
return <<HTML;
<html>
<body>
<img src="data:image/${image_type};base64,${image_base64}" />
</body>
</html>
HTML
}
my $image_path = "/tmp/test.jpg";
open(my $fh, '<', $image_path)
or die "unable to open file $image_path: $!";
binmode($fh);
my $image_data = do {local $/; <$fh>};
close($fh);
print return_page_content("jpeg", $image_data);