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;
}
controller
@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);
}
}
service
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工具类
/**
* 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));
}