Delusional has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I'm trying to send existing images (not generated) to a browser. The difference is, is the html code contains the <img ..> call, which calls a cgi, which then sends the image to the browser. The idea behind it, is to keep people from externally linking to the images. Thus far I have gotten nothing to work. The closest I have gotten was with an error from the server stating that the image file had a malformed headder (hmm its an image!).

So to help clairify:
In the html:
<img border="0" src="/cgi-bin/my.cgi?image=images/some.jpg" width="368 +" height="275">


In the cgi/perl, I tried:
print "Location: /cgi-bin/$in{image}\n\n"; # (I get malformed headder, but only on image files, zip or exe # files are sent (if I don't call using <img ..>))

And:
print "/cgi-bin/$in{image}"; # (I get nothing, no image, no error)
I have also tried using Content-type: headders, but they as well, do not help.

How can I get the cgi/perl to send the image correctly without using ssi or putting the <img ..> code in the cgi/perl file?

20050419 Cleaned up by Corion: Put code in code tags.

Replies are listed 'Best First'.
Re: Sending an image to a browser
by dorward (Curate) on Apr 19, 2005 at 10:01 UTC

    The 1999 HTML standard made the alt attribute for <img> non-optional. The Location header in HTTP must be an absolute URL, you are providing a relative URL. Neither of these are likely to be the cause of your problem as most browsers can error correct, but its still worth fixing the mistakes.

    What does $in{image} contain? Have you checked it contains what you expect? Putting warn $in{image} directly after you populate it might help in your debugging.

    What happens if you try to access the image directly? If you are redirecting to an image file stored under cgi-bin you might encounter problems if the server expects it to be an executable file.

    Exactly what HTTP headers are being output? You can look at them using lynx with something like lynx -dump -head http://www.example.com/cgi-bin/my.cgi?image=images/some.jpg.

    If you visit http://www.example.com/cgi-bin/my.cgi?image=images/some.jpg in your webbrowser, does it get visibly redirected? (i.e. does the URL in the addressbar change?)

      The Location header in HTTP must be an absolute URL, you are providing a relative URL.
      In most CGI implemenations (or at least, Apache), a "root-relative" url in a location header is interpreted by the webserver as an "internal redirect" - i.e. the server will return the content at that url, without informing the client.

      What I think is wrong is that in most server configurations it's not allowed to have anything but executables in the /cgi-bin/ location - that means the images should be put somewhere else.

Re: Sending an image to a browser
by bsdz (Friar) on Apr 19, 2005 at 10:00 UTC
    have you tried something like: -
    binmode(STDOUT); open(my $img, "images/some.jpg") or print "content-type: text/plain\n\nfailed to open image"; local $/; print "content-type: image/jpeg\n\n"; print <$img>; close $img;
    (This hasn't been tested)
Re: Sending an image to a browser
by Cody Pendant (Prior) on Apr 19, 2005 at 09:43 UTC
    I have also tried using Content-type: headders, but they as well, do not help.

    Which content-type headers did you use?



    ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
    =~y~b-v~a-z~s; print
Re: Sending an image to a browser
by samizdat (Vicar) on Apr 19, 2005 at 13:21 UTC
    I'd like to offer another, simpler alternative. If the goal is simply to prevent people from linking directly to your images, why not use regular HTML tags... to temporary image files? It's very easy to move files around or rename them.

    The best solution I've seen is to create symbolic links that you then destroy at some later time, either during your page exit Javascript, your next page CGI from an action button, or as a recurring housecleaning function. Check out symlink in your Perl docs. Caveat: If your web system is Doze-hosted, you'll have to move or rename files; symlinking doesn't work.
Re: Sending an image to a browser
by webchalkboard (Scribe) on Apr 19, 2005 at 10:40 UTC

    Hello,
    I don't know for sure, but it you are trying to stop people from using your images from another server, isn't there something you can do with a .htaccess file. I haven't done it myself, but you can be pretty strict about what is and isn't allowed to be requested from your server. So that route might be worth investigating...

    Tom

    Learning without thought is labor lost; thought without learning is perilous. - Confucius
    WebChalkboard.com | For the love of art...

      The specifics can be seen at http://www.allmyfaqs.com/faq.pl?Prevent_bandwidth_theft, but note that this will also block the images for users of certain privacy protecting proxies (although only ones which break the HTTP specification by replacing the Referer header with something invalid rather then removing it entirely)

      But then won't everyone have to enter a username and password to see the image (even when it's produced by the cgi)? This doesn't seem to be what the OP wanted.
      chas
      (Update: My error; the answer is "no".)

        I think you misunderstand the purpose of the .htaccess file - which is, simply put, to provide a per directory configuration. Please see the apache documentation if this is not clear.

        /J\

Re: Sending an image to a browser
by Delusional (Beadle) on Apr 20, 2005 at 09:44 UTC
    After some more trial and error, I was able to solve the situation. Thanks everyone for your help. The following are my comments to your recomendations.

    Cody Pendant:
    I used these:
    Content-type: image/gif and Content-type: image/jpeg . Both failed.

    bsdz:
    This, regretfully, didn't work.

    dorward:
    >What does $in{image} contain?
    The image file name to display.

    >Have you checked it contains what you expect?
    Yes, and it has the correct information (excluding the 'secret' dir).

    >Exactly what HTTP headers are being output?
    See above (Cody Pendant).

    >If you visit ..... visibly redirected?
    No, either get nothing, or an error about incorrect headders from the image file.

    pearlie:
    Thanks but, I'm not really a fan of perl OOP. I prefer the KISS moto, and sending my own information to the browser that way I know what's being sent. Yes, perl OOP makes life simpler, but I simply keep to what I know works and write my own headders.

    zentara:
    The browser fills in the URL, so /cgi-bin/my.cgi?image=images/some.jpg becomes http://www.example.com/cgi-bin/my.cgi?image=images/some.jpg. Leaving off the / at the begining, through things 'out of wack' and I end up with cgi-bin calles where they shouldn't be. Keeping the / at the begining ensures that the call accesses the cgi-bin where I expect it to.

    dwildesnl:
    Thanks, however, I didn't want to do this. I do have space limits on the server, and simply wanted to move the images to a new location and have the cgi serve them from there to keep people from being able to link directly to them. Granted they can link to the them via the cgi, however, I can set the cgi to refuse access to the images if the refer isn't from my site. The refer should always be present in this case, and from my site, since the images being called are from within the page. I've done some testing (not through though), and the result is always that the image call has the refer information.

    So, to the solution:
    The information from dorward got me thinking. I moved the images to a different dir outside of the cgi-bin, and then went through the different tests I had preformed until I found out what was needed. The solution isn't exactly what I disired, however it works. I simply use print "Location: /svrimgs/$in{image}\n\n"; and presto, the images show up. No headder needed. Most likely I can chmod the dir in the cgi-bin with the right tags and/or set the .htaccess with the right information, and then the cgi can serve the images from the cgi-bin. Its something to play with, but using an images dir that only the cgi script knows about, should keep the images from being hot-linked, which is the first of two goals (the second being having the cgi only serve the real image if the refer tag contains the right information, if not, serve another image stating the image can only be accessed from the site directly. Draw back is, is like dorward explains, where firewalls remove the refer tag. But, we can't have everything our way).

    Thanks all.
Re: Sending an image to a browser
by pearlie (Sexton) on Apr 19, 2005 at 11:03 UTC
    Hello, i think if use object-oriented style of CGI, then you must be creating a CGI object using  my $q = new CGI(); print $q->header(); print $q->start_html(); etc... Once you do that, you can use  print $q->img( { -src => "filename"} ); to print an image.
      But then, using "view source" in the browser, one will see the link to the image, which is what the OP wished to avoid, I believe.
      chas
A reply falls below the community's threshold of quality. You may see it by logging in.