图片上传--工作中
需求:在app接口文档中编写头像上传的接口;
1、在controller中传入人员的id以及头像,调用service层编写的头像上传的功能,代码如下:
@ApiOperation("头像上传") @PostMapping("/saveHeadImageUrl") public Result saveHeadImageUrl(HeadImageBean bean) { if (null == bean || DataUtil.isEmpty(bean.getImage()) || DataUtil.isEmpty(bean.getEmpId())) { return new Result(StatusCode.RET_BAD_REQUEST); } String s = workerService.uploadImage(bean.getEmpId(), bean.getImage()); return new Result(s); }
2、进入uploadImage方法,同样传入的是工人id以及头像。new一个MultipartFile数组对象,长度为1,将图片传入数组中。此时,需要调用UploadImageUtil这个工具类,该工具类包括FTP上传下载的工具类、host、port、username、password,后面四个是进入远程ftp的参数。FTP上传下载的工具类代码如下:
package org.fiberhome.smartsiteapp.util.ftp; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; import org.fiberhome.smartsiteapp.config.FtpException; import org.fiberhome.smartsiteapp.util.DataUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Comparator; /** * FTP上传下载 * */ @Component public class FtpUtil { protected final static Logger logger = LoggerFactory.getLogger(FtpUtil.class); private FTPClient ftpClient = null; private String filePath; /** * 获取FTP连接 * * @param port * @param username * @param password * @return * @throws FtpException */ public void init(String host, int port, String username, String password) throws FtpException { if (ftpClient == null) { ftpClient = new FTPClient(); } try { // 连接FTP服务器 ftpClient.connect(host, port); } catch (Exception e) { throw new FtpException("FTP[" + host + ":" + port + "]连接失败!", e); } logger.info("ftpClient.getReplyCode()="+String.valueOf(ftpClient.getReplyCode())); // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器 if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { try { ftpClient.login(username, password); } catch (Exception e) { throw new FtpException("FTP用户[" + username + "]登陆失败!", e); } } else { throw new FtpException("FTP连接出错!"); } logger.info("用户[" + username + "]登陆[" + host + "]成功."); try { // 设置被动模式 ftpClient.enterLocalPassiveMode(); ftpClient.setFileTransferMode(FTPClient.STREAM_TRANSFER_MODE); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); } catch (Exception e) { logger.error("", e); throw new FtpException("FTP初始化出错!", e); } } /** * 关闭FTP客户端 * * @throws Exception */ public void close(String username,String host) throws FtpException { try { ftpClient.logout(); logger.info("用户[" + username + "]退出[" + host + "]成功."); } catch (IOException e) { logger.error("", e); ftpClient = null; throw new FtpException("FTP退出登录出错!", e); } } /** * 上传 * * @param fileName 上传目录 * @param in 本地目录 * @return * @throws Exception */ public String uploadFile(String fileName ,InputStream in) throws Exception { try { setPath(filePath, true); ftpClient.storeFile(fileName, in); logger.info("[" + fileName + "]上传成功!"); return filePath+"/"+fileName; } catch (IOException e) { logger.error("", e); throw new FtpException("FTP上传[" + fileName + "]出错!", e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } } /** 获得目录下最大文件名 */ public String getMaxFileName(String remotePath) { try { ftpClient.changeWorkingDirectory(remotePath); FTPFile[] files = ftpClient.listFiles(); Arrays.sort(files, new Comparator<FTPFile>() { public int compare(FTPFile o1, FTPFile o2) { return o2.getName().compareTo(o1.getName()); } }); return files[0].getName(); } catch (IOException e) { logger.error("", e); throw new FtpException("FTP访问目录[" + remotePath + "]出错!", e); } } public void setPath(String path, boolean createIfNotExists) throws Exception { if (DataUtil.isEmpty(path)) { return; } String[] ss = path.split("/"); path = "/"; for (int i = 0; i < ss.length; i++) { if (!DataUtil.isEmpty(ss[i])) { try { path = path + ss[i] + "/"; int r = ftpClient.cwd(path); if ((!FTPReply.isPositiveCompletion(r)) && (createIfNotExists)) { ftpClient.makeDirectory(path); ftpClient.cwd(path); } } catch (Exception e) { if (createIfNotExists) { ftpClient.makeDirectory(path); ftpClient.cwd(path); } else { throw e; } } } } ftpClient.setFileType(2); } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } }
调用UploadImageUtil时,将MultipartFile数组对象中存的头像传入文件上传处理的方法内。
在工作中遇到的是为了使图片路径的唯一化,所以是以域名+项目名+时间+UUID生成的随机数+图片格式,比如:http://yl.fiberhomecloudnj.com:7084/SmartSiteApp/2019/10/19/be40cd28-7f92-4f8e-a915-de0637a1c2e8.jpg。当然这是生成图片最终的结果;
下面就是一步步的实现该路径:
1、这是为图片路径进行初始化
DateTimeFormatter dt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String format = LocalDate.now().format(dt);
StringBuilder filePath = new StringBuilder("pub/");
filePath.append("SmartSiteApp/" + format);
/*设置FTP属性*/
ftpClient.setFilePath(filePath.toString());
2、登录远程ftp服务器:传入host、port、username、password
public void init(String host, int port, String username, String password) throws FtpException { if (ftpClient == null) { ftpClient = new FTPClient(); } try { // 连接FTP服务器 ftpClient.connect(host, port); } catch (Exception e) { throw new FtpException("FTP[" + host + ":" + port + "]连接失败!", e); } logger.info("ftpClient.getReplyCode()="+String.valueOf(ftpClient.getReplyCode())); // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器 if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { try { ftpClient.login(username, password); } catch (Exception e) { throw new FtpException("FTP用户[" + username + "]登陆失败!", e); } } else { throw new FtpException("FTP连接出错!"); } logger.info("用户[" + username + "]登陆[" + host + "]成功."); try { // 设置被动模式 ftpClient.enterLocalPassiveMode(); ftpClient.setFileTransferMode(FTPClient.STREAM_TRANSFER_MODE); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); } catch (Exception e) { logger.error("", e); throw new FtpException("FTP初始化出错!", e); } }
3、上传图片具体操作:
在图片路径初始化的基础上加上UUID生成的随机数,生成例如的路径SmartSiteApp/2019/10/19/be40cd28-7f92-4f8e-a915-de0637a1c2e8.jpg,代码如下:
Map<String, Object> fileMap = new HashMap<>(); MultipartFile f = files[i]; String name = f.getOriginalFilename(); //后缀名唯一化,方便在数据库中进行更新操作 String uuid = UUID.randomUUID().toString(); String postFix = name.substring(name.lastIndexOf(".")).toLowerCase(); //图片后缀名,压缩用 String fileEnd = name.substring(name.lastIndexOf(".") + 1).toLowerCase(); String fileName = uuid + postFix;
在原有的基础上,将原图片按照原尺寸压缩图片,具体方法实现是按照流实现的
//原图片 BufferedImage imgsrc = ImageIO.read(f.getInputStream()); // 原尺寸压缩图片 BufferedImage newImg = disposeImage(imgsrc, imgsrc.getWidth(), imgsrc.getHeight()); bs = new ByteArrayOutputStream(); imOut = ImageIO.createImageOutputStream(bs); ImageIO.write(newImg, fileEnd, imOut); InputStream is = new ByteArrayInputStream(bs.toByteArray()); fileName = ftpClient.uploadFile(fileName, is).replace("pub", "");
其中已经通过调用upLoadFile将图片传入ftp服务器对应的目录下,然后通过拼接路径返回到上一级
/** * 上传 * * @param fileName 上传目录 * @param in 本地目录 * @return * @throws Exception */ public String uploadFile(String fileName ,InputStream in) throws Exception { try { setPath(filePath, true); ftpClient.storeFile(fileName, in); logger.info("[" + fileName + "]上传成功!"); return filePath+"/"+fileName; } catch (IOException e) { logger.error("", e); throw new FtpException("FTP上传[" + fileName + "]出错!", e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } }
将已经获得的路径名塞入map中,然后返回service层
/*上传*/ List<Map<String, Object>> maps = uploadImageUtil.uploadImageToFtp(multipartFiles); String filePath = "#ftp#" + maps.get(0).get("filePath").toString(); /*存库*/ int i = workerMapper.updateHeadImage(empId, filePath); return urlBase + filePath.replaceAll("#ftp#", "");
其中#ftp#是根据工作中数据库中存储图片路径的规范,将图片路径存入数据库后,就是将公网地址替代#ftp#,形成最后的访问上传头像的地址。
其中UploadImageUtil的代码如下:
package org.fiberhome.smartsiteapp.util; import org.fiberhome.smartsiteapp.config.FtpException; import org.fiberhome.smartsiteapp.util.ftp.FtpUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.*; /** * 文件上传 * * @author lxc * @date 2019/7/22 */ @Component public class UploadImageUtil { @Resource private FtpUtil ftpClient; @Value("${ftp.host}") private String host; @Value("${ftp.port}") private int port; @Value("${ftp.name}") private String username; @Value("${ftp.password}") private String password; /** * 上传文件处理(支持批量),并且将图片大小进行压缩 */ public List<Map<String, Object>> uploadImageToFtp(MultipartFile[] files) { List<Map<String, Object>> file = new ArrayList<Map<String, Object>>(); DateTimeFormatter dt = DateTimeFormatter.ofPattern("yyyy/MM/dd"); String format = LocalDate.now().format(dt); StringBuilder filePath = new StringBuilder("pub/"); filePath.append("SmartSiteApp/" + format); /*设置FTP属性*/ ftpClient.setFilePath(filePath.toString()); System.out.println(host + "----" + port + "----" + username + "----" + password); //登录远程ftp服务器 ftpClient.init(host, port, username, password); /*上传图片*/ for (int i = 0; i < files.length; i++) { Map<String, Object> fileMap = new HashMap<>(); MultipartFile f = files[i]; String name = f.getOriginalFilename(); //后缀名唯一化,方便在数据库中进行更新操作 String uuid = UUID.randomUUID().toString(); String postFix = name.substring(name.lastIndexOf(".")).toLowerCase(); //图片后缀名,压缩用 String fileEnd = name.substring(name.lastIndexOf(".") + 1).toLowerCase(); String fileName = uuid + postFix; ImageOutputStream imOut = null; ByteArrayOutputStream bs = null; try { //原图片 BufferedImage imgsrc = ImageIO.read(f.getInputStream()); // 原尺寸压缩图片 BufferedImage newImg = disposeImage(imgsrc, imgsrc.getWidth(), imgsrc.getHeight()); bs = new ByteArrayOutputStream(); imOut = ImageIO.createImageOutputStream(bs); ImageIO.write(newImg, fileEnd, imOut); InputStream is = new ByteArrayInputStream(bs.toByteArray()); fileName = ftpClient.uploadFile(fileName, is).replace("pub", ""); fileMap.put("filePath", fileName); file.add(fileMap); } catch (Exception e) { throw new FtpException("上传失败"); } } ftpClient.close(username, host); return file; } /** * 上传文件处理(支持批量),并且将图片大小进行压缩 */ // public List<Map<String, Object>> uploadImageWithFtp(HttpServletRequest request) { // CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( // request.getSession().getServletContext()); // List<Map<String, Object>> file = new ArrayList<Map<String, Object>>(); // if (!multipartResolver.isMultipart(request)) { // throw new FtpException("非法参数"); // } // MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; // Iterator<String> iterator = multiRequest.getFileNames(); // // String serverName = multiRequest.getParameter("serverName"); // SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd"); // String date = format.format(new Date()); // StringBuilder filePath = new StringBuilder("pub/"); // if (null == serverName && serverName.equals("")) { // filePath.append(date); // } else { // filePath.append(serverName + "/" + date); // } // ftpClient.setFilePath(filePath.toString()); // System.out.println(host + "----" + port + "----" + username + "----" + password); // ftpClient.init(host, port, username, password); // while (iterator.hasNext()) { // String key = iterator.next(); // MultipartFile multipartFile = multiRequest.getFile(key); // if (multipartFile != null) { // Map<String, Object> fileMap = new HashMap<>(); // String name = multipartFile.getOriginalFilename(); // fileMap.put("fileName", name); // String fileType = multipartFile.getContentType(); // fileMap.put("fileType", fileType); // // long fileBytes = multipartFile.getSize(); // fileMap.put("fileBytes", fileBytes); // if (name.indexOf(".") == -1 && "blob".equals(name)) { // name = name + ".png"; // } // String uuid = UUID.randomUUID().toString(); // String postFix = name.substring(name.lastIndexOf(".")).toLowerCase(); // //图片后缀名,压缩用 // String fileEnd = name.substring(name.lastIndexOf(".") + 1).toLowerCase(); // String fileName = uuid + postFix; // ImageOutputStream imOut = null; // ByteArrayOutputStream bs = null; // try { // //原图片 // BufferedImage imgsrc = ImageIO.read(multipartFile.getInputStream()); // // 原尺寸压缩图片 // BufferedImage newImg = disposeImage(imgsrc, imgsrc.getWidth(), imgsrc.getHeight()); // bs = new ByteArrayOutputStream(); // imOut = ImageIO.createImageOutputStream(bs); // ImageIO.write(newImg, fileEnd, imOut); // InputStream is = new ByteArrayInputStream(bs.toByteArray()); // fileName = ftpClient.uploadFile(fileName, is).replace("pub", ""); // fileMap.put("filePath", fileName); // file.add(fileMap); // } catch (Exception e) { // throw new FtpException("上传失败"); // } // } // } // ftpClient.close(username, host); // return file; // } /** * 压缩图片 * * @param src * @param width * @param height * @return */ private synchronized static BufferedImage disposeImage(BufferedImage src, int width, int height) { BufferedImage newImg = null; // 判断输入图片的类型 switch (src.getType()) { case 13: // png,gifnewImg = new BufferedImage(new_w, new_h, // BufferedImage.TYPE_4BYTE_ABGR); break; default: newImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); break; } Graphics2D g = newImg.createGraphics(); // 从原图上取颜色绘制新图 g.drawImage(src, 0, 0, width, height, null); g.dispose(); // 根据图片尺寸压缩比得到新图的尺寸 newImg.getGraphics().drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); return newImg; } }