一,为什么要给图片生成缩略图?
1, 用户上传的原始图片如果太大,不能直接展示在网站页面上,
因为不但流费server的流量,而且用户打开时非常费时间,
所以要生成缩略图。
2,服务端管理图片要注意的几点:
第一点:缩略图要与原图分开存储,
然后通过符号链接方式允许前端访问,
否则原图被直接访问仍然存在浪费流量的问题,
有图片版权的公司也不允许外部直接访问原图
第二点:图片名字要加上一些随机的数字,
避免被穷举访问
第三点:图片要分目录存放,通常每1000张保存到一个目录,
因为大量的图片如果存放到同一个目录下,
打开时会非常慢或者不能打开,
不利于我们对文件进行管理
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,项目的地址:
https://github.com/liuhongdi/imagemodify
2,项目原理:
实现了图片的文件上传,
并用ImageMagick给非gif图片生成缩略图,
用ffmpeg生成gif图片的缩略图
3,项目结构:如图:
三,ImageMagick/ffmpeg工具软件的安装
1,dnf安装ImageMagick
[root@blog head]# dnf install ImageMagick
说明:注意软件包名字中I和M均为大写
检查软件是否安装成功?
[root@blog head]# whereis convert convert: /usr/bin/convert /usr/share/man/man1/convert.1.gz
2,安装ffmpeg
安装rpmfusion库
[root@blog ~]# dnf install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
下载sdl库
[root@blog ~]# wget http://mirror.centos.org/centos/8/PowerTools/x86_64/os/Packages/SDL2-2.0.8-7.el8.x86_64.rpm
安装sdl库:
[root@blog ~]# rpm -ivh SDL2-2.0.8-7.el8.x86_64.rpm Verifying... ################################# [100%] Preparing... ################################# [100%] Updating / installing... 1:SDL2-2.0.8-7.el8 ################################# [100%]
安装ffmpeg
[root@blog ~]# dnf install ffmpeg
四,配置文件说明:
1,application.properties
#upload spring.servlet.multipart.maxFileSize=30MB spring.servlet.multipart.maxRequestSize=30MB
说明:指定上传文件的最大限制,
默认大小是1M
2,nginx访问文件的host配置:file.conf
server { listen 81; server_name file.lhdtest.com; root /data/file/html; location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { root /data/file/html; expires 24h; } index index.html; access_log /data/logs/nginxlogs/file.access_log; error_log /data/logs/nginxlogs/file.error_log; }
3,Constant.java
//图片从nginx访问时的host public static final String IMAGES_URL_HOST = "http://127.0.0.1:81"; //默认原始图片的路径 public static final String IMAGES_ORIG_DIR = "/data/file/html/images"; //缩略图的文件路径 public static final String IMAGES_TMB_DIR = "/data/file/html/tmb"; //缩略图的长边长度 public static final String IMAGES_TMB_LONG = "300"; //分页显示时每页的显示数量 public static final int IMAGES_PAGE_SIZE = 5; //ImageMagick命令的安装路径 public static final String IMAGEMAGICK_DIR = "/usr/bin"; //ffmpeg的完整路径 public static final String FFMPEG_CMD = "/usr/bin/ffmpeg";
4,创建保存图片信息的数据表:
CREATE TABLE `image_service` ( `image_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT \'图片id\', `image_sn` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT \'图片编号\', `image_type` varchar(10) NOT NULL DEFAULT \'\' COMMENT \'图片类型\', `title` varchar(200) NOT NULL DEFAULT \'\' COMMENT \'标题\', `size` int(11) NOT NULL DEFAULT \'0\' COMMENT \'文件大小\', `width` int(11) NOT NULL DEFAULT \'0\' COMMENT \'宽度\', `height` int(11) NOT NULL DEFAULT \'0\' COMMENT \'高度\', `add_time` datetime NOT NULL DEFAULT \'2019-11-01 01:01:01\' COMMENT \'添加时间 \', `staff_id` int(11) NOT NULL DEFAULT \'0\' COMMENT \'添加的用户\', PRIMARY KEY (`image_id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=\'图片信息表\'
五,java代码说明:
1,ImageModifyUtil.java
public class ImageModifyUtil { //按指定的边长生成缩略图 public static boolean image_resize_by_long_side(String orig_path, String dest_path, String long_size,String imageType) { try { List<String> command = new ArrayList<>(); String command_one = ""; if (imageType.equals("gif")) { //计算得到目标宽高 File gifFile = new File(orig_path); int gifWidth = 0; int gifHeight = 0; try { BufferedImage imageBuffer = ImageIO.read(gifFile); if (imageBuffer != null) {//如果image=null 表示上传的不是图片格式 gifWidth = imageBuffer.getWidth(); gifHeight = imageBuffer.getHeight(); } } catch (IOException e) { e.printStackTrace(); } int destWidth = 0; int destHeight = 0; if (gifWidth > gifHeight) { destWidth = Integer.parseInt( long_size ); destHeight = (destWidth*gifHeight) / gifWidth; } else if (gifWidth == gifHeight) { destWidth = Integer.parseInt( long_size ); destHeight = destWidth; } else { destHeight = Integer.parseInt( long_size ); destWidth = (destHeight*gifWidth) / gifHeight; } command_one = Constant.FFMPEG_CMD+" -i "+orig_path+" -s "+destWidth+"x"+destHeight+" "+dest_path+" 2>&1"; } else { command_one = Constant.IMAGEMAGICK_DIR+"/convert -size "+long_size+"x"+long_size+" -resize "+long_size+"x"+long_size+" +profile \'*\' -quality 85 "+orig_path+" "+dest_path+" 2>&1"; } //System.out.println(command_one); command.add("sh"); command.add("-c"); command.add(command_one); // 执行cmd命令 ProcessBuilder builder = new ProcessBuilder(); builder.command(command); Process process = builder.start(); return true; } catch (Exception e) { System.out.println("save ioexception"); e.printStackTrace(); return false; } } }
这个类用来生成缩略图
说明:gif图生成缩略图时,我们要帮助ffmpeg计算出准确的长和宽
2,ImageDownUtil.java
public class ImageDownUtil { //按指定的路径下载一张图片 public static void downImageByLocalPath(HttpServletResponse response,String fullImagePath,String imageName) { File file = new File(fullImagePath); if (file.exists()) { response.setContentType("application/force-download");// 设置强制下载不打开 response.addHeader("Content-Disposition", "attachment;fileName=" + imageName);// 设置文件名 byte[] buffer = new byte[1024]; FileInputStream fis = null; BufferedInputStream bis = null; try { fis = new FileInputStream(file); bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = bis.read(buffer); while (i != -1) { os.write(buffer, 0, i); i = bis.read(buffer); } System.out.println("success"); } catch (Exception e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } } //按指定的文件路径显示图片 public static ResponseEntity<InputStreamResource> dispImageByLocalPath(String fullImagePath) { HttpHeaders header = new HttpHeaders(); header.setContentType(MediaType.IMAGE_JPEG); Path path = Paths.get(fullImagePath); InputStream content; try { content = Files.newInputStream(path); return new ResponseEntity<>(new InputStreamResource(content), header, HttpStatus.OK); } catch (IOException e) { e.printStackTrace(); return ResponseEntity.notFound().build(); } } }
这个类用来下载图片和用java显示一张图片
六,效果测试
测试前的注意事项:
测试前要先启动服务:
mysql
nginx
1,图片上传:
访问:
http://127.0.0.1:8080/image/imageadd
如图:
2,查看已上传的图片列表
访问:
http://127.0.0.1:8080/image/imagelist
如图:
七,查看spring boot的版本
. ____ _ __ _ _ /\\ / ___\'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | \'_ | \'_| | \'_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) \' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.1.RELEASE)