分类
代码

服务器端动态输出图像

摘要

本文记录了服务器端(php,java)直接输出图像的一种方法。可用于论坛签名实时显示IP及时间。

java版效果:

Servlet输出IP到图像
Servlet输出IP到图像

php版图像效果:
IP

Key Words: 动态输出图像, IP输出到图像,

引言

今天整理虚拟主机空间里的文件时,发现根目录有一个ip.php文件,一时忘了是啥,一看内容才发现是早些时候写的php输出图像的小程序片段。
最早是在Dospy.com塞班论坛的用户[变号]的论坛签名上看到过。(2012年)可以在[这里]查看论坛签名效果。好吧其实就是一个图像:
IP

PHP版

于是可能在去年某个时候,我上塞班论坛时看到了变号君的签名,就想着用php试试能不能实现。
下面是当时通过搜索互联网,写出来的代码:

   <?php
	header("Content-type: image/png");
	//创建图像http://www.php.net/imagecreate
	$im = imagecreate(130,30);
	//http://www.php.net/ImageColorAllocate
	$background_color = ImageColorAllocate ($im, 255, 255, 255); 
	unset($ip);
	if($_SERVER['HTTP_CLIENT_IP']){
		$ip=$_SERVER['HTTP_CLIENT_IP'];
	} else if($_SERVER['HTTP_X_FORWARDED_FOR']){
		$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
	} else{
		$ip=$_SERVER['REMOTE_ADDR'];
	}
	//设置颜色http://www.php.net/imagecolorallocate
	$col = imagecolorallocate($im, 0, 51, 102);
	//画字符串http://www.php.net/imagestring
	imagestring($im, 3, 5, 1, $ip , $col);
	$ip="By Youth.Lin";
	imagestring($im, 3, 15, 15, $ip , $col);
	//输出图像http://www.php.net/imagegif
	imagegif($im);
	imagedestroy($im);
	?>

出处已不可考,或许是我从网上东拼西凑出来的。直接看代码也挺直观的。
缺点:文件名是.php后缀,而不是图像的后缀。

2016-01-28更新:
使用.htaccess的URL重写规则,使得访问ip.png实际上是访问ip.php

第一步:开启Apache服务器的重写模块

打开 Apache 的配置文件 httpd.conf 文件,找到下面一行:
#LoadModule rewrite_module modules/mod_rewrite.so
去掉#号注释。

第二步:新建.htaccess文件

Linux可以直接新建,Windows需要使用记事本的另存为,类型选择所有文件,一般编码选择UTF8(不能直接重命名)。

第三步:编写伪静态规则

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^ip\.png$ /ip.php [L]
</IfModule>

^ip\.png$^脱帽符号表示匹配开头, \.点号使用反斜杠转义 $美元符号表示匹配结尾。 [L]表示停止处理接下来的规则

参见

用.htaccess文件实现URL重写

Java版

正好目前在学Java Web, 于是今天我打算用java来也试试实现一下。
用JSP的Servlet 匹配(mapping) URL 这样就可以是任意文件名了。
首先我们需要设置输出类型是图像,然后创建一幅图像,把IP及日期画在图像上,最后输出图像即可。(下方右上角可以选择新窗口看代码)

    import javax.imageio.ImageIO;
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import java.awt.*;
	import java.awt.font.FontRenderContext;
	import java.awt.geom.Rectangle2D;
	import java.awt.image.BufferedImage;
	import java.awt.image.ImageConsumer;
	import java.io.IOException;
	import java.io.OutputStream;
	import java.text.SimpleDateFormat;
	import java.util.Date;
	
	/**
	 * Created by lin on 2016-01-27-027.
	 */
	@WebServlet(name = "IPServlet", value = "/ip.png")
	public class IPServlet extends HttpServlet {
		    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	        doGet(request, response);
	    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    //设置文件类型
        response.setContentType("image/png");
        //获取参数
        String w = request.getParameter("w");
        String h = request.getParameter("h");
        String s = request.getParameter("s");
        String c = request.getParameter("c");
        int width = 300, height = 60, size = 18;
        if (s != null) {
            try {//使用try-catch块防止出错
                size = Integer.parseInt(s);
            } catch (Exception e) {
            }
        }

        Font font = new Font("微软雅黑", Font.PLAIN, size);
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D gd = img.createGraphics();
        gd.setFont(font);

        String ip = getIp(request);
        //ip = "1234::1234::1234::1234::1234::1234::1234::1234";
        Date date = new Date();
        //Java String和Date的转换
        //http://www.cnblogs.com/bmbm/archive/2011/12/06/2342264.html
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss (z) E");
        String d = sdf.format(date);
        String me = "Powered By Youth.霖";

        double ipW = getWidth(font, ip, gd.getFontRenderContext()),
                ipH = getHeight(font, ip, gd.getFontRenderContext()),
                dateW = getWidth(font, d, gd.getFontRenderContext()),
                dateH = getHeight(font, d, gd.getFontRenderContext()),
                meW = getWidth(font, me, gd.getFontRenderContext()),
                meH = getHeight(font, me, gd.getFontRenderContext());

        width = (int) ipW;
        if (dateW > width) width = (int) dateW;
        if (meW > width) width = (int) meW;
        width += 2;//留一点点边距
        height = (int) (ipH + dateH + meH + 2);

        if (w != null) {
            width = Integer.parseInt(w);
        }
        if (h != null) {
            height = Integer.parseInt(h);
        }

        //Java生成透明背景图片
        //http://snkcxy.iteye.com/blog/1872229
        img = gd.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        gd = img.createGraphics();
        gd.setFont(font);
        //随机颜色
        Color color = new Color(
                (new Double(Math.random() * 128)).intValue() + 128,
                (new Double(Math.random() * 128)).intValue() + 128,
                (new Double(Math.random() * 128)).intValue() + 128);
        if (c != null) {
            try {
                //Java中颜色的String和Color对象之间的互相转换
                //http://winhack.iteye.com/blog/1843781
                color = new Color(Integer.parseInt(c, 16));
            } catch (Exception e) {
            }
        }
        gd.setColor(color);
        //画矩形框
        gd.drawRect(0, 0, width - 1, height - 1);
		//画字符串drawString(string,x,y)
		//左起x像素,从上往下y像素这个(x,y)是字符串的左下角坐标(不是左上角)
        gd.drawString(d, (float) ((width - dateW) / 2), (float) (height / 3));
        gd.drawString(ip, (float) ((width - ipW) / 2), (float) (height / 3 * 2));
        gd.drawString(me, (float) (width - meW) / 2, (float) (height - 3));
        ImageIO.write(img, "png", response.getOutputStream());
	    }

	    /**
	     * //计算字符串的像素长度与高度
	     * //http://blog.chinaunix.net/uid-79084-id-97205.html
		     * Graphics2D g = (Graphics2D)Toolkit.getDefaultToolkit().getImage("imgname").getGraphics();
	     * //设置大字体
	     * Font font = new Font("楷体", Font.ITALIC | Font.BOLD, 72);
	     * g.setFont(font);
	     * FontRenderContext context = g.getFontRenderContext();
	     * //获取字体的像素范围对象
	     * Rectangle2D stringBounds = font.getStringBounds("text", context);
	     * double fontWidth = stringBounds.getWidth();
	     */
	    private double getWidth(Font f, String str, FontRenderContext context) {
	        return f.getStringBounds(str, context).getWidth();
	    }
	
	    private double getHeight(Font f, String s, FontRenderContext c) {
	        return f.getStringBounds(s, c).getHeight();
	    }
	    
	 //JSP 获取真实IP地址的代码
	 //http://www.jb51.net/article/21272.htm
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
	}

在浏览器输入 http://localhost:8080/Project-Name/ip.png 就能看到效果了。

类名使用了注解配置URL mapping,就不用到web.xml里配置了。先使用预定义的宽高创建了图像,然后设置画笔的字体,这样之后就可以计算字符串在这个字体下的像素大小了。生成重新透明背景的图像,使用相同的字体设置画笔,再设置颜色,把字符串画上图像,最后输出图像。

参考

见代码中注释的链接。


“服务器端动态输出图像”上的7条回复

这个在静态、动态验证码的应用倒是相当广泛。
在php里header定位到某个图像也可以实现随机图片。
不知道伪静态和header一块用会有啥效果,准备试试……

貌似我曾也用PHP试过给QQ空间插的图片做动态改变。记得是得加载PHP的什么GD画图的库。
我想到一个,现在浏览器支持SVG图像,所以我想,可以支持用代码输出XML代码浏览器解析成失量图像,对于你此文的输出IP、时间这类内容真是再合适不过了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

[/鼓掌] [/难过] [/调皮] [/白眼] [/疑问] [/流泪] [/流汗] [/撇嘴] [/抠鼻] [/惊讶] [/微笑] [/得意] [/大兵] [/坏笑] [/呲牙] [/吓到] [/可爱] [/发怒] [/发呆] [/偷笑] [/亲亲]