Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单。
用的是smms图床。
依赖:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.12</version> </dependency>
@Data public class PictureDto implements Serializable { /** ID */ private Long id; /** 上传日期 */ private Timestamp createTime; /** 删除的URL */ private String deleteUrl; /** 图片名称 */ private String filename; /** 图片高度 */ private String height; /** 图片大小 */ private String size; /** 图片地址 */ private String url; /** 用户名称 */ private String username; /** 图片宽度 */ private String width; /** 文件的MD5值 */ private String md5code; }
@RestController @RequestMapping("/api/pictures") @Api(tags = "工具:免费图床管理") public class PictureController { private final IGenerator generator; private final PictureService pictureService; public PictureController(IGenerator generator, PictureService pictureService) { this.generator = generator; this.pictureService = pictureService; } @Log("查询图片") @PreAuthorize("@el.check('pictures:list')") @GetMapping @ApiOperation("查询图片") public ResponseEntity<Object> getRoles(PictureQueryCriteria criteria, Pageable pageable) { return new ResponseEntity<>(pictureService.queryAll(criteria, pageable), HttpStatus.OK); } @Log("导出数据") @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('pictures:list')") public void download(HttpServletResponse response, PictureQueryCriteria criteria) throws IOException { pictureService.download(generator.convert(pictureService.queryAll(criteria), PictureDto.class), response); } @Log("上传图片") @PreAuthorize("@el.check('pictures:add')") @PostMapping @ApiOperation("上传图片") public ResponseEntity<Object> upload(@RequestParam MultipartFile file) { String userName = SecurityUtils.getUsername(); Picture picture = pictureService.upload(file, userName); return new ResponseEntity<>(picture, HttpStatus.OK); } @Log("同步图床数据") @ApiOperation("同步图床数据") @PostMapping(value = "/synchronize") public ResponseEntity<Object> synchronize() { pictureService.synchronize(); return new ResponseEntity<>(HttpStatus.OK); } @Log("多选删除图片") @ApiOperation("多选删除图片") @PreAuthorize("@el.check('pictures:del')") @DeleteMapping public ResponseEntity<Object> deleteAll(@RequestBody Long[] ids) { pictureService.deleteAll(ids); return new ResponseEntity<>(HttpStatus.OK); } }
package co.yixiang.tools.service.impl; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import co.yixiang.common.service.impl.BaseServiceImpl; import co.yixiang.common.utils.QueryHelpPlus; import co.yixiang.dozer.service.IGenerator; import co.yixiang.exception.BadRequestException; import co.yixiang.tools.domain.Picture; import co.yixiang.tools.service.PictureService; import co.yixiang.tools.service.dto.PictureDto; import co.yixiang.tools.service.dto.PictureQueryCriteria; import co.yixiang.tools.service.mapper.PictureMapper; import co.yixiang.utils.FileUtil; import co.yixiang.utils.TranslatorUtil; import co.yixiang.utils.ValidationUtil; import co.yixiang.utils.YshopConstant; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.*; // 默认不使用缓存 //import org.springframework.cache.annotation.CacheConfig; //import org.springframework.cache.annotation.CacheEvict; //import org.springframework.cache.annotation.Cacheable; /** * @author hupeng * @date 2020-05-13 */ @Service //@AllArgsConstructor //@CacheConfig(cacheNames = "picture") @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class PictureServiceImpl extends BaseServiceImpl<PictureMapper, Picture> implements PictureService { private final IGenerator generator; // IGenerator是一个自定义的数据转化接口 @Value("${smms.token}") private String token;//是用于图床上传文件的token //在yml文件中配置: //# sm.ms 图床的 token //smms: // token: 1oOP3ykFDI0K6ifmtvU7c8Y1eTWZSlyl private static final String SUCCESS = "success"; private static final String CODE = "code"; private static final String MSG = "message"; public PictureServiceImpl(IGenerator generator) { this.generator = generator; } @Override //@Cacheable public Map<String, Object> queryAll(PictureQueryCriteria criteria, Pageable pageable) { getPage(pageable); PageInfo<Picture> page = new PageInfo<>(queryAll(criteria)); Map<String, Object> map = new LinkedHashMap<>(2); map.put("content", generator.convert(page.getList(), PictureDto.class)); map.put("totalElements", page.getTotal()); return map; } @Override //@Cacheable public List<Picture> queryAll(PictureQueryCriteria criteria) { return baseMapper.selectList(QueryHelpPlus.getPredicate(Picture.class, criteria)); } @Override public void download(List<PictureDto> all, HttpServletResponse response) throws IOException { List<Map<String, Object>> list = new ArrayList<>(); for (PictureDto picture : all) { Map<String, Object> map = new LinkedHashMap<>(); map.put("上传日期", picture.getCreateTime()); map.put("删除的URL", picture.getDeleteUrl()); map.put("图片名称", picture.getFilename()); map.put("图片高度", picture.getHeight()); map.put("图片大小", picture.getSize()); map.put("图片地址", picture.getUrl()); map.put("用户名称", picture.getUsername()); map.put("图片宽度", picture.getWidth()); map.put("文件的MD5值", picture.getMd5code()); list.add(map); } FileUtil.downloadExcel(list, response); } @Override @Transactional(rollbackFor = Throwable.class) public Picture upload(MultipartFile multipartFile, String username) { File file = FileUtil.toFile(multipartFile); // 验证是否重复上传 Picture picture = this.getOne(new LambdaQueryWrapper<Picture>().eq(Picture::getMd5code, FileUtil.getMd5(file))); if (picture != null) { return picture; } HashMap<String, Object> paramMap = new HashMap<>(1); paramMap.put("smfile", file); // 上传文件 String result = HttpRequest.post(YshopConstant.Url.SM_MS_URL + "/v2/upload") .header("Authorization", token) .form(paramMap)//设置map类型表单数据 .timeout(20000) .execute().body();//获取响应主体 JSONObject jsonObject = JSONUtil.parseObj(result); if (!jsonObject.get(CODE).toString().equals(SUCCESS)) { throw new BadRequestException(TranslatorUtil.translate(jsonObject.get(MSG).toString())); } picture = JSON.parseObject(jsonObject.get("data").toString(), Picture.class); picture.setSize(FileUtil.getSize(Integer.parseInt(picture.getSize()))); picture.setUsername(username); picture.setMd5code(FileUtil.getMd5(file)); picture.setFilename(FileUtil.getFileNameNoEx(multipartFile.getOriginalFilename()) + "." + FileUtil.getExtensionName(multipartFile.getOriginalFilename())); this.save(picture); //删除临时文件 FileUtil.del(file); // FileUtil是PathUtil的子类 是cn.hutool.core.io包下 return picture; } @Override public Picture findById(Long id) { Picture picture = this.getById(id); ValidationUtil.isNull(picture.getId(), "Picture", "id", id); return picture; } @Override public void deleteAll(Long[] ids) { for (Long id : ids) { Picture picture = findById(id); try { HttpUtil.get(picture.getDeleteUrl()); this.removeById(id); } catch (Exception e) { this.removeById(id); } } } @Override public void synchronize() { //链式构建请求 //Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单 String result = HttpRequest.get(YshopConstant.Url.SM_MS_URL + "/v2/upload_history") //头信息,多个头信息多次调用此方法即可 .header("Authorization", token) .timeout(20000) .execute().body(); JSONObject jsonObject = JSONUtil.parseObj(result); List<Picture> pictures = JSON.parseArray(jsonObject.get("data").toString(), Picture.class); for (Picture picture : pictures) { if (this.getOne(new LambdaQueryWrapper<Picture>().eq(Picture::getUrl, picture.getUrl())) == null) { picture.setSize(FileUtil.getSize(Integer.parseInt(picture.getSize()))); picture.setUsername("System Sync"); picture.setMd5code(null); this.save(picture); } } } }
public interface IGenerator { /** * 转换 * * @param s 数据对象 * @param clz 复制目标类型 * @return {@link T} * @Description: 单个对象的深度复制及类型转换,vo/domain , po * @author banjuer@outlook.com * @Time 2018年5月9日 下午3:53:24 */ <T, S> T convert(S s, Class<T> clz); /** * 转换 * @param s 数据对象 * @param clz 复制目标类型 * @return {@link List<T>} * @Description: list深度复制 * @author banjuer@outlook.com * @Time 2018年5月9日 下午3:54:08 */ <T, S> List<T> convert(List<S> s, Class<T> clz); } @Component @Lazy(true) public class EJBGenerator implements IGenerator { @Autowired protected Mapper dozerMapper; //Mapper 是org.dozer包下的 @Override public <T, S> T convert(final S s, Class<T> clz) { return s == null ? null : this.dozerMapper.map(s, clz); } @Override public <T, S> List<T> convert(List<S> s, Class<T> clz) { return s == null ? null : s.stream().map(vs -> this.dozerMapper.map(vs, clz)).collect(Collectors.toList()); } }
/** * File工具类,扩展 hutool 工具包 * @author Zheng Jie * @date 2018-12-27 */ public class FileUtil extends cn.hutool.core.io.FileUtil { /** * 定义GB的计算常量 */ private static final int GB = 1024 * 1024 * 1024; /** * 定义MB的计算常量 */ private static final int MB = 1024 * 1024; /** * 定义KB的计算常量 */ private static final int KB = 1024; /** * 格式化小数 */ private static final DecimalFormat DF = new DecimalFormat("0.00"); /** * MultipartFile转File */ public static File toFile(MultipartFile multipartFile) { // 获取文件名 String fileName = multipartFile.getOriginalFilename(); // 获取文件后缀 String prefix = "." + getExtensionName(fileName); File file = null; try { // 用uuid作为文件名,防止生成的临时文件重复 file = File.createTempFile(IdUtil.simpleUUID(), prefix); // MultipartFile to File multipartFile.transferTo(file); } catch (IOException e) { e.printStackTrace(); } return file; } /** * 获取文件扩展名,不带 . */ public static String getExtensionName(String filename) { if ((filename != null) && (filename.length() > 0)) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length() - 1))) { return filename.substring(dot + 1); } } return filename; } /** * Java文件操作 获取不带扩展名的文件名 */ public static String getFileNameNoEx(String filename) { if ((filename != null) && (filename.length() > 0)) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length()))) { return filename.substring(0, dot); } } return filename; } /** * 文件大小转换 */ public static String getSize(long size) { String resultSize; if (size / GB >= 1) { //如果当前Byte的值大于等于1GB resultSize = DF.format(size / (float) GB) + "GB "; } else if (size / MB >= 1) { //如果当前Byte的值大于等于1MB resultSize = DF.format(size / (float) MB) + "MB "; } else if (size / KB >= 1) { //如果当前Byte的值大于等于1KB resultSize = DF.format(size / (float) KB) + "KB "; } else { resultSize = size + "B "; } return resultSize; } /** * 将文件名解析成文件的上传路径 */ public static File upload(MultipartFile file, String filePath) { //String name = getFileNameNoEx(file.getOriginalFilename()); String suffix = getExtensionName(file.getOriginalFilename()); StringBuffer nowStr = fileRename(); try { String fileName = nowStr + "." + suffix; String path = filePath + fileName; // getCanonicalFile 可解析正确各种路径 File dest = new File(path).getCanonicalFile(); // 检测是否存在目录 if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } // 文件写入 file.transferTo(dest); return dest; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 导出excel */ public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException { String tempPath = System.getProperty("java.io.tmpdir") + IdUtil.fastSimpleUUID() + ".xlsx"; File file = new File(tempPath); BigExcelWriter writer = ExcelUtil.getBigWriter(file); // 一次性写出内容,使用默认样式,强制输出标题 writer.write(list, true); //response为HttpServletResponse对象 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); ServletOutputStream out = response.getOutputStream(); // 终止后删除临时文件 file.deleteOnExit(); writer.flush(out, true); //此处记得关闭输出Servlet流 IoUtil.close(out); } public static String getFileType(String type) { String documents = "txt doc pdf ppt pps xlsx xls docx"; String music = "mp3 wav wma mpa ram ra aac aif m4a"; String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; if (image.contains(type)) { return "pic"; } else if (documents.contains(type)) { return "txt"; } else if (music.contains(type)) { return "music"; } else if (video.contains(type)) { return "vedio"; } else { return "other"; } } public static void checkSize(long maxSize, long size) { // 1M int len = 1024 * 1024; if (size > (maxSize * len)) { throw new BadRequestException("文件超出规定大小"); } } /** * 判断两个文件是否相同 */ public static boolean check(File file1, File file2) { String img1Md5 = getMd5(file1); String img2Md5 = getMd5(file2); return img1Md5.equals(img2Md5); } /** * 判断两个文件是否相同 */ public static boolean check(String file1Md5, String file2Md5) { return file1Md5.equals(file2Md5); } private static byte[] getByte(File file) { // 得到文件长度 byte[] b = new byte[(int) file.length()]; try { InputStream in = new FileInputStream(file); try { in.read(b); } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); return null; } return b; } private static String getMd5(byte[] bytes) { // 16进制字符 char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(bytes); byte[] md = mdTemp.digest(); int j = md.length; char[] str = new char[j * 2]; int k = 0; // 移位 输出字符串 for (byte byte0 : md) { str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 下载文件 * @param request / * @param response / * @param file / */ public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { response.setCharacterEncoding(request.getCharacterEncoding()); response.setContentType("application/octet-stream"); FileInputStream fis = null; try { fis = new FileInputStream(file); response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); IOUtils.copy(fis, response.getOutputStream()); response.flushBuffer(); } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); if (deleteOnExit) { file.deleteOnExit(); } } catch (IOException e) { e.printStackTrace(); } } } } public static String getMd5(File file) { return getMd5(getByte(file)); }