文章目录
* 前言 <https://blog.csdn.net/qq_26788593/article/details/85062168#_1>
* 判断上传的是否是图片 <https://blog.csdn.net/qq_26788593/article/details/85062168#_4>
* 通过后缀名进行判断 <https://blog.csdn.net/qq_26788593/article/details/85062168#_5>
* 通过文件头 <https://blog.csdn.net/qq_26788593/article/details/85062168#_21>
* 通过ImageIO判断
<https://blog.csdn.net/qq_26788593/article/details/85062168#ImageIO_83>
* 图片文件的安全检查处理
<https://blog.csdn.net/qq_26788593/article/details/85062168#_113>
* 给图片加水印 <https://blog.csdn.net/qq_26788593/article/details/85062168#_115>
<>前言
在web开发中,肯定会有一些图片上传的功能,如果仅仅是通过页面端进行控制是远远不够的,完全可以直接调用后台的接口,将一些病毒文件上传到服务器,如果不进行校验,后果不堪设想!
<>判断上传的是否是图片
<>通过后缀名进行判断
这层校验应该说是最基本的校验了,看下文件的后缀名是否符合要求的格式。
String fileType= ""; int i = fileName.lastIndexOf('.'); if (i > 0) { fileType=
fileName.substring(i+1); } //... if("jpg".equals(fileType) ||
"png".equals(fileType) ....){ //your code }
这种非常的不靠谱,完全可以修改文件的后缀名绕过检验。
<>通过文件头
根据文件的前面几个字节,即常说的魔术数字进行判断,不同文件类型的开头几个字节
// 获得文件头部字符串 public static String bytesToHexString(byte[] src) { StringBuilder
stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) {
return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF;
String hv = Integer.toHexString(v); if (hv.length() < 2) {
stringBuilder.append(0); } stringBuilder.append(hv); } return
stringBuilder.toString(); }
不同文件的头魔术数字
private static void getAllFileType() { FILE_TYPE_MAP.put("jpg", "FFD8FF");
//JPEG FILE_TYPE_MAP.put("png", "89504E47"); //PNG FILE_TYPE_MAP.put("gif",
"47494638"); //GIF FILE_TYPE_MAP.put("tif", "49492A00"); //TIFF
FILE_TYPE_MAP.put("bmp", "424D"); //Windows Bitmap FILE_TYPE_MAP.put("dwg",
"41433130"); //CAD FILE_TYPE_MAP.put("html", "68746D6C3E"); //HTML
FILE_TYPE_MAP.put("rtf", "7B5C727466"); //Rich Text Format
FILE_TYPE_MAP.put("xml", "3C3F786D6C"); FILE_TYPE_MAP.put("zip", "504B0304");
FILE_TYPE_MAP.put("rar", "52617221"); FILE_TYPE_MAP.put("psd", "38425053");
//PhotoShop FILE_TYPE_MAP.put("eml", "44656C69766572792D646174653A"); //Email
[thorough only] FILE_TYPE_MAP.put("dbx", "CFAD12FEC5FD746F"); //Outlook Express
FILE_TYPE_MAP.put("pst", "2142444E"); //Outlook FILE_TYPE_MAP.put("office",
"D0CF11E0"); //office类型,包括doc、xls和ppt FILE_TYPE_MAP.put("mdb",
"000100005374616E64617264204A"); //MS Access FILE_TYPE_MAP.put("wpd",
"FF575043"); //WordPerfect FILE_TYPE_MAP.put("eps", "252150532D41646F6265");
FILE_TYPE_MAP.put("ps", "252150532D41646F6265"); FILE_TYPE_MAP.put("pdf",
"255044462D312E"); //Adobe Acrobat FILE_TYPE_MAP.put("qdf", "AC9EBD8F");
//Quicken FILE_TYPE_MAP.put("pwl", "E3828596"); //Windows Password
FILE_TYPE_MAP.put("wav", "57415645"); //Wave FILE_TYPE_MAP.put("avi",
"41564920"); FILE_TYPE_MAP.put("ram", "2E7261FD"); //Real Audio
FILE_TYPE_MAP.put("rm", "2E524D46"); //Real Media FILE_TYPE_MAP.put("mpg",
"000001BA"); // FILE_TYPE_MAP.put("mov", "6D6F6F76"); //Quicktime
FILE_TYPE_MAP.put("asf", "3026B2758E66CF11"); //Windows Media
FILE_TYPE_MAP.put("mid", "4D546864"); //MIDI (mid) }
此时有人把一个可执行的PHP文件的扩展名修改为PNG,然后再在前面补上”89 50″两个字节,就又绕开了这种验证方式,这种也是不靠谱的!
<>通过ImageIO判断
*
通过ImageReader来解码这个file并返回一个BufferedImage对象,如果找不到合适的ImageReader则会返回null,我们可以认为这不是图片文件。
* 另外如果能够正常的获取到一张图片的宽高属性,那肯定这是一张图片,因为非图片文件我们是获取不到它的宽高属性的。 /** *
通过读取文件并获取其width及height的方式,来判断判断当前文件是否图片,这是一种非常简单的方式。 * @param imageFile *
@return */ public static boolean isImage(File imageFile) { if
(!imageFile.exists()) { return false; } Image img = null; try { img =
ImageIO.read(imageFile); if (img == null || img.getWidth(null) <= 0 ||
img.getHeight(null) <= 0) { return false; } return true; } catch (Exception e)
{ return false; } finally { img = null; } }
这种方式较安全!
<>图片文件的安全检查处理
通过上面的方法,确认上传的文件是图片了,但是如果在可以正常打开的图片里面加入非法代码或者病毒,那就非常危险了。那么怎么可以预防这种情况,既能够正常打开,又能获取图片的宽高等属性,可以对图片进行重写,新生成的图片不会有这种恶意代码了。
<>给图片加水印
/** * 给图片添加水印、可设置水印图片旋转角度 * @param iconPath 水印图片路径 * @param srcImgPath 源图片路径
* @param targerPath 目标图片路径 * @param degree 水印图片旋转角度 * @param width 宽度(与左相比) *
@param height 高度(与顶相比) * @param clarity 透明度(小于1的数)越接近0越透明 */ public static void
waterMarkImageByIcon(String iconPath, String srcImgPath, String targerPath,
Integer degree, Integer width, Integer height, float clarity) { OutputStream os
= null; try { Image srcImg = ImageIO.read(new File(srcImgPath));
System.out.println("width:" + srcImg.getWidth(null));
System.out.println("height:" + srcImg.getHeight(null)); BufferedImage buffImg =
new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
BufferedImage.TYPE_INT_RGB); // 得到画笔对象 Graphics2D g = buffImg.createGraphics();
// 设置对线段的锯齿状边缘处理 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(
srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null),
Image.SCALE_SMOOTH), 0, 0, null); if (null != degree) { // 设置水印旋转
g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double)
buffImg.getHeight() / 2); } // 水印图象的路径 水印一般为gif或者png的,这样可设置透明度 ImageIcon
imgIcon = new ImageIcon(iconPath); // 得到Image对象。 Image img =
imgIcon.getImage(); float alpha = clarity; // 透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); //
表示水印图片的位置 g.drawImage(img, width, height, null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g.dispose(); os = new FileOutputStream(targerPath); // 生成图片
ImageIO.write(buffImg, "JPG", os); System.out.println("添加水印图片完成!"); } catch
(Exception e) { e.printStackTrace(); } finally { try { if (null != os)
os.close(); } catch (Exception e) { e.printStackTrace(); } } } /** *
给图片添加水印、可设置水印图片旋转角度 * @param logoText 水印文字 * @param srcImgPath 源图片路径 * @param
targerPath 目标图片路径 * @param degree 水印图片旋转角度 * @param width 宽度(与左相比) * @param
height 高度(与顶相比) * @param clarity 透明度(小于1的数)越接近0越透明 */ public static void
waterMarkByText(String logoText, String srcImgPath, String targerPath, Integer
degree, Integer width, Integer height, Float clarity) { // 主图片的路径 InputStream
is = null; OutputStream os = null; try { Image srcImg = ImageIO.read(new
File(srcImgPath)); BufferedImage buffImg = new
BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
BufferedImage.TYPE_INT_RGB); // 得到画笔对象 // Graphics g= buffImg.getGraphics();
Graphics2D g = buffImg.createGraphics(); // 设置对线段的锯齿状边缘处理
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(
srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null),
Image.SCALE_SMOOTH), 0, 0, null); if (null != degree) { // 设置水印旋转
g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double)
buffImg.getHeight() / 2); } // 设置颜色 g.setColor(Color.red); // 设置 Font
g.setFont(new Font("宋体", Font.BOLD, 30)); float alpha = clarity;
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); //
第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y) . g.drawString(logoText, width, height);
g.dispose(); os = new FileOutputStream(targerPath); // 生成图片
ImageIO.write(buffImg, "JPG", os); System.out.println("添加水印文字完成!"); } catch
(Exception e) { e.printStackTrace(); } finally { try { if (null != is)
is.close(); } catch (Exception e) { e.printStackTrace(); } try { if (null !=
os) os.close(); } catch (Exception e) { e.printStackTrace(); } } } public
static void main(String[] args) throws IOException {
waterMarkImageByIcon("d:/shuiyin.png", imagePath, "d:/result.png", 10, 100,
100, 0F); waterMarkByText("logo", imagePath, "d:/result1.png", 3, 100, 100,
0F); }
上面加水印,你可以将透明度调为0,乍一看跟原图一样,其实不是上面的那张原图了。
效果展示
原图:
加水印图片(水印透明度0.5):
加水印文字(水印透明度0.5):
热门工具 换一换