草庐IT

php - 在 PHP 中加水印以及存储和显示图像的正确方法

coder 2024-04-10 原文

我正在构建一个基于网络的系统,它将承载大量高分辨率图像,并且它们将可供出售。当然,我永远不会显示高分辨率图像,而是人们在浏览时只会看到低分辨率、带水印的图像。目前工作流程如下:

PHP 脚本处理高分辨率图像上传,上传图像时,它会自动调整为低分辨率图像和缩略图,并且两个文件都保存在服务器上,(不添加水印) .

当人们浏览时,页面显示图像的缩略图,点击时,它会放大并显示带有水印的低分辨率图像。目前,每当打开低分辨率图像时,我都会即时应用水印。

我的问题是,正确的方法是什么:

1) 我应该只在第一次访问时保存带缩略图的低分辨率图像的第二个副本吗?我的意思是,如果有人访问图像,我会即时添加水印,然后显示图像并将其存储在服务器上。下次访问同一图像时,如果存在水印副本,则只显示 wm 副本,否则即时应用水印。 (如果 watermark.png 被更改,只需删除带水印的图像,它们将在访问时重新创建)。

2) 我是否应该像现在这样不断地动态应用水印。

我最大的问题是 PHP file_exists() 与向图像添加水印之间的区别有多大,例如:

$image = new Imagick();
$image->readImage($workfolder.$event . DIRECTORY_SEPARATOR . $cat . DIRECTORY_SEPARATOR .$mit);
$watermark = new Imagick();
$watermark->readImage($workfolder.$event . DIRECTORY_SEPARATOR . "hires" . DIRECTORY_SEPARATOR ."WATERMARK.PNG");
$image->compositeImage($watermark, imagick::COMPOSITE_OVER, 0, 0);

所有低分辨率图像均为 1024x1024,JPG,质量设置为 45%,并删除了所有不必要的滤镜,因此低分辨率图像的文件大小约为 40Kb-80Kb。

它在某种程度上与 this question 有关,只是规模和场景有点不同。

我使用的是专用服务器 (Xeon E3-1245v2) cpu、32 GB 内存、2 TB 存储空间),该站点的总体流量并不大,但时不时会出现巨大的峰值。当图像发布时,我们每小时有几千次点击,人们浏览图像、下载、购买等。所以在正常使用时,我确信即时生成是正确的方法,我有点担心关于高峰期。

需要说明的是,我使用的是 ImageMagick 库进行图像处理,而不是 GD。

感谢您的输入。

更新

没有一个答案是完整的解决方案,但这很好,因为我从未寻找过。这是一个艰难的决定,接受哪个人以及给谁赏金。

@Ambroise-Maupate 的解决方案很好,但它需要依靠 PHP 来完成这项工作。

@Hugo Delsing 提议使用网络服务器来提供缓存文件,减少对 PHP 脚本的调用,这意味着使用的资源更少,另一方面,它并不是真正的存储友好型。

我将使用 2 个答案的混合合并解决方案,依靠 CRON 作业来清除垃圾。

谢谢指点。

最佳答案

就我个人而言,我会以某种 CDN 方式创建静态/无 cookie 子域来处理此类图像。主要原因是:

  1. 图像只创建一次
  2. 只创建访问过的图像
  3. 创建后,图像将从缓存中提供,速度要快得多。

第一步是在指向空文件夹的子域上创建一个网站。使用 IIS/Apache 或其他任何设置来禁用此新网站的 session 。还要在站点上设置一些长缓存 header ,因为内容不应该改变

第二步是创建一个包含以下内容的 .htaccess 文件。

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)   /create.php?path=$1  [L]

这将确保如果有人访问现有图像,它会直接显示图像而不会受到 PHP 的干扰。每个不存在的请求都将由 create.php 脚本处理,这是您接下来应该添加的内容。

<?php
function NotFound()
{
    if (!headers_sent()) {
        $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
        header($protocol . ' 404 Not Found');
        echo '<h1>Not Found</h1>';
        exit;
    }
}

$p = $_GET['path'];

//has path
if (strlen($p)<=1)
    NotFound();

$clean = explode('?', $p);
$clean = explode('#', $clean[0]);
$params = explode('/', substr($clean[0], 1)); //drop first /

//I use a check for two, because I dont allow images in the root folder
//I also use the path to determine how it should look
//EG: thumb/125/90/imagecode.jpg
if (count($params)<2)
    NotFound();

$type = $params[0];

//I use the type to handle different methods. For this example I only used the full sized image
//You could use the same to handle thumbnails or cropped/watermarked
switch ($type) {
    //case "crop":if (Crop($params)) return; else break;
    //case "thumb":if (Thumb($params)) return; else break;
    case "image":if (Image($params)) return; else break;
}
NotFound();
?>
<?php
/*
Just some example to show how you could create a responds
Since you already know how to create thumbs, I'm not going into details

Array
(
    [0] => image
    [1] => imagecode.JPG
)
*/
function Image($params) {
    $tmp = explode('.', $params[1]);
    if (count($tmp)!=2)
        return false;

    $code = $tmp[0];


    //WARNING!! SQL INJECTION
    //USE PROPER DB METHODS TO GET REALPATH, THIS IS JUST EXAMPLE
    $query = "SELECT realpath FROM images WHERE Code='".$code."'";
    //exec query here to $row
    $realpath = $row['realpath'];


    $f = file_get_contents($realpath);

    if (strlen($f)<=0)
        return false;

    //create folder structure
    @mkdir($params[0]);

    //if you had more folders, continue creating the structure
    //@mkdir($params[0].'/'.$params[1]);

    //store the image, so a second request won't access this script
    file_put_contents($params[0].'/'.$params[1], $f);

    //you could directly optimize the image for web to make it even better
    //optimizeImage($params[0].'/'.$params[1]);

    //now serve the file to the browser, because even the first request needs to show the image
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    header('Content-Type: '.finfo_file($finfo, $params[0].'/'.$params[1]));

    echo $f;

    return true;
}
?>

关于php - 在 PHP 中加水印以及存储和显示图像的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28022890/

有关php - 在 PHP 中加水印以及存储和显示图像的正确方法的更多相关文章

  1. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  5. ruby-on-rails - link_to 不显示任何 rails - 2

    我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article

  6. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  7. ruby-on-rails - 复数 for fields_for has_many 关联未显示在 View 中 - 2

    目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi

  8. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. ruby-on-rails - 在 Flash 警报 Rails 3 中显示错误消息 - 2

    如果我在模型中设置验证消息validates:name,:presence=>{:message=>'Thenamecantbeblank.'}我如何让该消息显示在闪光警报中,这是我迄今为止尝试过的方法defcreate@message=Message.new(params[:message])if@message.valid?ContactMailer.send_mail(@message).deliverredirect_to(root_path,:notice=>"Thanksforyourmessage,Iwillbeintouchsoon")elseflash[:error]

随机推荐