您的位置:首页 > Web前端 > JavaScript

JSP 实用程序之简易图片服务器

2016-05-28 10:35 591 查看
最初 node 版本在这里
源码下载,单 jsp 文件 http://pan.baidu.com/s/1gfoDy0r (百度云提供)

App 客户端加载图片的时候,在服务端需要进行相关的优化,否则会给流量和占用内存带来消耗和压力。起初想到了利用 node 搭建一个简易的图片服务器来完成。遗憾的是,因为 Centos 平台上 node-images 二进制包的问题,导致不能顺利使用 node 方案。于是迫不得已,改用 Java 来完成。及后,考虑到逻辑不复杂和部署的简易,特意将 Java 改成 JSP 页面调用,而非 Servlet。JSP 大家都比较熟悉,这里再提一提它的优点:1、可以“过程式”编码,不用考虑包等的问题。虽然面向对象 OO 必须强调,但某些时候“过程式”足矣,结构简单明晰;2、无须重启服务器,修改立刻看到效果,减少运维的工作。有同学说过,JSP 问题:一、混搭 HTML 代码不好看,这个确实是如果开发者可以注意下,也是能够规避的;二、写起来太慢。噢~我现在不是第一时间在 JSP 里面写 Java,而是在 Java 类里面写好了、测试好了再 copy 到 JSP(原 Java 文件在这里),稍微改动下,有点像手动打包的过程,呵呵。
值得一提的是,Tomcat 7 下 JSP 默认的 Java 语法仍旧是 1.6 的。在 JSP 里面嵌入 Java 1.7 特性的代码会抛出“Resource specification not allowed here for source level below 1.7”的异常。于是需要修改 Tomcat/conf/web.xml 里面的配置文件,找到 <servlet> 节点,加入下面粗体部分才可以。注意是 jsp 节点,不是 default 节点(很相似)。
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
        <init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.7</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.7</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
调用例子:
img_resize.jsp?url=http://desk.fd.zol-img.com.cn/t_s1920x1200c5/g5/M00/0F/04/ChMkJ1dGqWOITU3xAA5PBuQ0iRMAAR6mAAAAAAADk8e103.jpg&h=400
先说说图片处理的流程。
首先从 url 参数获取图片地址,该参数必填

通过 HTTP HEAD 请求检测是否 404,以及图片文件大小
如果小于 100kb (1024 * 100)则无须压缩,响应对象发出 302 重定向 url 地址
如果大于 100kb,则读取文件流,输入到 java.awt.Image 对象
如果没有指定高、宽,那么直接在浏览器输出图片(原尺寸输出)

若 w / h 指定其一则按图片比例缩放,w:int = 宽度,可选,h:init = 高度,可选。
通过图片原尺寸计算出比例,求得 w / h
通过 setResize(Image img, int height, int width) 方法设置图片大小

可能还有考虑不周的情况,望大家指点提拨。
JSP 页面调用方式就是:
<%@page pageEncoding="utf-8" import="java.io.*, java.net.*, javax.imageio.ImageIO, java.awt.Image, java.awt.image.*"%>
<%
// JSP 实用程序之简易图片服务器
// http://blog.csdn.net/zhangxin09/article/details/51523489 // sp42 2016-5-30
String url = request.getParameter("url");
if(url == null){
out.println("缺少 url 参数!");
return;
}
try{
getImg(url, request, response);
//清除输出流,防止释放时被捕获异常
out.clear();
out = pageContext.pushBody();
} catch(Exception e) {
out.println("Error:" + e);
}
%>

依赖函数仅仅是以下几个:

<%!
/**
* 主要的函数
*/
public static void getImg(String url, HttpServletRequest request,  HttpServletResponse response) throws IOException {
System.out.println("请求地址:" + url);
URL urlObj = new URL(url);
HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();

int imgSize = getSize(conn, urlObj.getHost());
if (imgSize < (1024 * 100)) {
response.sendRedirect(url);// 发送重定向
return;
} else {
//		System.out.println("BigImg");

String imgType = getImgType(url);

response.setContentType(getContentType(imgType));
try (
InputStream is = new URL(url).openStream();
ServletOutputStream op = response.getOutputStream();
){
String height = request.getParameter("h"), width = request.getParameter("w");

if (height != null && width != null) {
BufferedImage bufImg = setResize(ImageIO.read(is), Integer.parseInt(height), Integer.parseInt(width));
ImageIO.write(bufImg, imgType, op);
} else if (height != null) {
Image img = ImageIO.read(is);// 将输入流转换为图片对象
int[] size = resize(img, Integer.parseInt(height), 0);

BufferedImage bufImg = setResize(img, size[0], size[1]);
ImageIO.write(bufImg, imgType, op);
} else if (width != null) {
Image img = ImageIO.read(is);// 将输入流转换为图片对象
int[] size = resize(img, 0, Integer.parseInt(width));

BufferedImage bufImg = setResize(img, size[0], size[1]);
ImageIO.write(bufImg, imgType, op);
} else {
// 直接写浏览器
byte[] buf = new byte[1024];
int len = is.read(buf, 0, 1024);
while (len != -1) {
op.write(buf, 0, len);
len = is.read(buf, 0, 1024);
}
}
}
}
}

/**
* 返回 Content type
* @param imgType
* @return
*/
private static String getContentType(String imgType) {
switch (imgType) {
case "jpg":
return "image/jpeg";
case "gif":
return "image/gif";
case "png":
return "image/png";
default:
return null;
}
}

/**
* 获取 url 最后的 .jpg/.png/.gif
* @param url
* @return
*/
private static String getImgType(String url) {
String[] arr = url.split("/");
arr = arr[arr.length - 1].split("\\.");
String t = arr[1];

return t.replace('.', ' ').trim().toLowerCase();
}

/**
* 缩放比例
*
* @param img
*            图片对象
* @param height
*            高
* @param width
*            宽
* @return
*/
public static int[] resize(Image img, int height, int width) {
int oHeight = img.getHeight(null), oWidth = img.getWidth(null);
double ratio = (new Integer(oHeight)).doubleValue() / (new Integer(oWidth)).doubleValue();

if(width != 0) {
height =  (int) (ratio * width);
}else {
width = (int) ( height / ratio);
}

return new int[]{height, width};
}

/**
* 完成设置图片大小
*
* @param img
*            图片对象
* @param height
*            高
* @param width
*            宽
* @return 缓冲的图片对象
*/
public static BufferedImage setResize(Image img, int height, int width) {
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bufImg.getGraphics().drawImage(img, 0, 0, width, height, null);

return bufImg;
}

/**
*
* @param conn
* @param referer
* @return
*/
public static int getSize(HttpURLConnection conn, String referer) {
try {
conn.setRequestMethod("HEAD");
conn.setInstanceFollowRedirects(false); // 必须设置false,否则会自动redirect到Location的地址
conn.addRequestProperty("Accept-Charset", "UTF-8;");
conn.addRequestProperty("User-Agent",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8");
conn.addRequestProperty("Referer", referer);
conn.setConnectTimeout(5000);// 设置超时
conn.setReadTimeout(30000);

InputStream body = null;

try {
body = conn.getInputStream();
} catch (UnknownHostException e) {
throw new RuntimeException("未知地址!" + conn.getURL().getHost() + conn.getURL().getPath());
} catch (FileNotFoundException e) {
throw new RuntimeException("404 地址!" + conn.getURL().getHost() + conn.getURL().getPath());
} catch (SocketTimeoutException e) {
throw new RuntimeException("请求地址超时!" + conn.getURL().getHost() + conn.getURL().getPath());
}

body.close();
// if (conn.getResponseCode() > 400) // 如果返回的结果是400以上,那么就说明出问题了
// LOGGER.warning("Err when got responseCode :" +
// conn.getResponseCode() + getUrlStr());

// conn.connect();也就是打开流
} catch (IOException e) {
e.printStackTrace();
return -1;
}
String sizeStr = conn.getHeaderField("content-length");

return Integer.parseInt(sizeStr);
}
%>

开发过程值得记住的几点:
Url 和 Connection 貌似不能重复使用,于是两次请求(同一地址)得有两个 url 对象
调整图片比例函数比较简单,但编码有时得晓得不是写算术。
设定好正确的 图片ContentType
不知道动图 gif 压缩后是否只剩一帧?

不足之处,敬请大家提出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: