服务器端动态输出图像

摘要

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

引言

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

PHP版

于是可能在去年某个时候,我上塞班论坛时看到了变号君的签名,就想着用php试试能不能实现。 下面是当时通过搜索互联网,写出来的代码:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   <?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(不能直接重命名)。

第三步:编写伪静态规则

1
2
3
4
<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及日期画在图像上,最后输出图像即可。(下方右上角可以选择新窗口看代码)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    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里配置了。先使用预定义的宽高创建了图像,然后设置画笔的字体,这样之后就可以计算字符串在这个字体下的像素大小了。生成重新透明背景的图像,使用相同的字体设置画笔,再设置颜色,把字符串画上图像,最后输出图像。

参考

见代码中注释的链接。

Reader Echoes

7 comments

  • #1334园子大魔王
    你的样式应该改下了
  • #1332c
    这个在静态、动态验证码的应用倒是相当广泛。 在php里header定位到某个图像也可以实现随机图片。 不知道伪静态和header一块用会有啥效果,准备试试……
  • #1331香菇肥牛
    这个功能好厉害,要是能用作头像的话就好玩了
  • #1329咸菜一点米
    貌似我曾也用PHP试过给QQ空间插的图片做动态改变。记得是得加载PHP的什么GD画图的库。 我想到一个,现在浏览器支持SVG图像,所以我想,可以支持用代码输出XML代码浏览器解析成失量图像,对于你此文的输出IP、时间这类内容真是再合适不过了。

表情

评论提交后需经审核才会显示。