I have a script that delivers an empty gif file when an attempt is made to download an image ("Save image as..."). Here is a link to my page. Try to save either of the first two images from the screen (not screenshots). I have been told that this is not possible and I am beginning to doubt my sanity. What do you think? Please comment if it does or does not work for you.

Click Here

As Priteas pointed out, you are stuck if a user decide to turn there browser script to false, I can still download what I want without permission, you need to add code that will prevent a download like maybe a login or something similar, maybe water marks etc.

Currently not even this simple JavaScript works since there are many errors , just open the console in development tools and you will see all those. Client side "save image" JavaScript blockers are a solution if someone that has no idea what internet is want to save an image. If has the slightest idea as priteas and AndreRet pointed could easily deactivate JavaScript.

Back to your main question if there is possible to prevent image downloading. There are several techniques to make saving images from your app a lot harder for someone. You will never eliminate it completely (because your server actually serves this content) but you can make it so hard that few would bother.

Serving the URL of your image in a data attribute (and a blank image in the source) , having a Shared JavaScript worker use the fetch API to createObjectURL and then on the main app js file on img onload revokeObjectURL would do the trick.

If you want to go deeper on it you could encode the URL in the data attribute and decode it in the shared worker so that no one that just reeds the HTML can see immediately the image URL. And would have to read the decode method in the shared worker to understand what is happening.

Even deeper , you could encode your image blob as it is served server side and decode this blob before using it to create object URL from blob in the shared JavaScript worker.

And finally you could have those two client side decoders in a wasm (Web Assembly) file hiding the implementation.
But even then you don't completely prevent someone from saving your image file , you just making it really hard.

Here's another way to download those images. It's the page info view in Firefox. Example:

image_2023-11-20_151854095.png

While I used another method in my last example, I thought I'd share a method that isn't very challenging for the end user.

The method you are using may block one method but my opinion is it's a waste of effort due to how easy it is to get the images from said page.

I've re-posted the page under discussion. I guess the method I propose is akin to disabling right click but instead it allows a user to go through the process of "save file as..." and all they get is a transparent .gif. There is no javascript involved.
Click Here
I'm quite aware that the source code can be viewed to enable direct access to an image.

I didn't use view source code. See example above. But if you feel it's good enough for you, then carry on.

My thought is that clean code may help in other ways such as page speed, SEO and the fact it's clean and easy to maintain.

20180406-_15A0049_(1).jpg

I downloaded it on my first attempt.
Right click.
View source
find image link
click on it.
Save it.

Accidentally downloaded it twice!

file name was 20180406-_15A0049.jpg, file size 527kb (the image with the door)

Easy Peasy.

If you send the data to my browser, I can read it in view source and by-pass your script totally.

I agree with everyone else, you can never prevent downloading, since the browser has to download, but you can make it harder for people to save and re-use the images. Here's a PHP script using standard PHP GD library, nothing special. It will break the image down into small chunks and then generate HTML to load them in the browser. So viewing source or inspecting the "image" will show up as a huge amount of really small images with totally random names. So reassembly would be tricky unless they did a lot of work copying the original HTML source to reassemble them.

Please don't flame me on the code. I know it can be improved for performance and other reasons, this is just an example of a possible solution, and it does work.

<?php

# the below width and height are set static and work out with no extra width or height
# from the source image.
# in production, it would probably be better to dynamically create the height and width 
# using a method to make sure there is no space left over
# or track the space left over in the loop and adjust the specs
# this is just for an example of the requirements of the OP

$width = 30; 
$height = 30; 
$sourceFile = "https://sample-videos.com/img/Sample-jpg-image-50kb.jpg";
$source = @imagecreatefromjpeg( $sourceFile ); 
$source_width = imagesx( $source ); 
$source_height = imagesy( $source ); 
$randomStringLength = 32;

$numberOfPixels = (($source_height * $source_width) / ($width * $height));
if ($numberOfPixels > 1000) {
    echo "change the width and height, because this will take forever to generate\n";
    exit;
}

function getName($n) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';

    for ($i = 0; $i < $n; $i++) {
        $index = rand(0, strlen($characters) - 1);
        $randomString .= $characters[$index];
    }

    return $randomString;
}

$images = [];
$seq = 1; #used to guarantee unique filenames, you can use another random generator instead if you wanted

for( $row = 0; $row < $source_height / $height; $row++) { 
    for( $col = 0; $col < $source_width / $width; $col++) { 
        $fn = getName($randomStringLength) . $seq . ".jpg";
        $images[] = $fn;
        $im = @imagecreatetruecolor( $width, $height ); 
        imagecopyresized( $im, $source, 0, 0, $col * $width, $row * $height, $width, $height, $width, $height ); 
        imagejpeg( $im, $fn ); 
        imagedestroy( $im ); 
        $seq++;
    } 
}

$template = <<<TEMP
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        margin: 0;
      }
      img {
        margin: 0;
        padding: 0;
      }
      div {
        display: flex;
        flex-wrap: wrap;
        height: {$source_height}px;
        width: {$source_width}px;
      }
    </style>
  </head>
  <body>
    <div>
TEMP;

foreach ($images as $image) {
    $template .= "<img src=\"$image\" width=\"$width\" height=\"$height\">";
}

$template .= <<<TEMP
    </div>
    <br><br><br>
    <img src="$sourceFile">
  </body>
</html>
TEMP;

$f = fopen("index.html", "w");
fwrite($f, $template);

echo "done\n";
commented: Creative solution! +34

The above php is using this image. Just thought I'd harvest that as well...

Sample-jpg-image-50kb.jpg

Of course, finding the php code that includes the image would be a lot harder!

Try to disable javascript

Another solution might be to just show smaller, low resolution versions of images, and only the full resolution if the person has paid for it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.