前端展示:
现在的需求是可以让管理人员上传图片 以及缩略图的生成方便商品展示时候可以进行展示 这样一个接口要怎么实现

思路:
因为要展示图片最后肯定要返回图片路径给前端,让前端可以找到图片存储的位置,同时也要为上传的图片存储到本地,为了便于区分以及习惯,文件夹以年月份的形式区分,文件名用随机字符串,缩略图添加_****作为区分
即文件结构为:设定的头路径+年月份+具体的文件名字+文件类型尾缀
controller层实现:
@RequestMapping("/uploadImage")
public ResponseVO uploadCover(@NotNull MultipartFile file, Boolean createThumbnail) throws IOException {
String filePath = fileUtils.uploadImage(file, createThumbnail);
return getSuccessResponseVO(filePath);
}
为什么不创建一个fileService或者fileMapper呢 因为对图片的处理无关数据库层面,所以只创建一个fileUtils方法类去存储方法
实现层:
public String uploadImage(MultipartFile file, Boolean createThumbnail) throws IOException {
String folderName = DateUtil.format(new Date(), DateTimePatternEnum.YYYYMM.getPattern()) + "/";
String folderPath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + folderName;
File folder = new File(folderPath);
if (!folder.exists()) {
folder.mkdirs();
}
String fileName = StringTools.getRandomString(Constants.LENGTH_30);
String suffix = StringTools.getFileSuffix(file.getOriginalFilename());
String resultFileName = null;
resultFileName = fileName + suffix;
file.transferTo(new File(folderPath + resultFileName));
if (createThumbnail != null && createThumbnail) {
String thumbnail = fileName + Constants.IMAGE_THUMBNAIL_SUFFIX + suffix;
createImageThumbnail(folderPath + resultFileName, folderPath + thumbnail);
return folderName + thumbnail;
}
return folderName + resultFileName;
}
讲解:
String folderName = DateUtil.format(new Date(), DateTimePatternEnum.YYYYMM.getPattern()) + "/";
String folderPath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + folderName;
File folder = new File(folderPath);
if (!folder.exists()) {
folder.mkdirs();
}
这一段就是为了把设定的头路径+年月份给弄出来,因为缩略图的名字和文件的名字不一样所以到这里之前他们的文件路径是相同的
String fileName = StringTools.getRandomString(Constants.LENGTH_30);
String suffix = StringTools.getFileSuffix(file.getOriginalFilename());
String resultFileName = null;
resultFileName = fileName + suffix;
file.transferTo(new File(folderPath + resultFileName));
这一段就是处理file本身的文件名以及尾椎
if (createThumbnail != null && createThumbnail) {
String thumbnail = fileName + Constants.IMAGE_THUMBNAIL_SUFFIX + suffix;
createImageThumbnail(folderPath + resultFileName, folderPath + thumbnail);
return folderName + thumbnail;
}
return folderName + resultFileName;
这一段是处理缩略图的,缩略图的方法如下 不用刻意学习 网上都可以搜到 是ffmpeg的方法
private void createImageThumbnail(String filePath, String thumbnailPath) {
final String CMD_CREATE_IMAGE_THUMBNAIL = "ffmpeg -i \"%s\" -vf scale=200:-1 \"%s\"";
String cmd = String.format(CMD_CREATE_IMAGE_THUMBNAIL, filePath, thumbnailPath);
ProcessUtils.executeCommand(cmd, true);
}
public class ProcessUtils {
private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
private static final String osName = System.getProperty("os.name").toLowerCase();
public static String executeCommand(String cmd, Boolean showLog) throws BusinessException {
if (StringTools.isEmpty(cmd)) {
return null;
}
Runtime runtime = Runtime.getRuntime();
Process process = null;
try {
//判断操作系统
if (osName.contains("win")) {
process = Runtime.getRuntime().exec(cmd);
} else {
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});
}
// 执行ffmpeg指令
// 取出输出流和错误流的信息
// 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住
PrintStream errorStream = new PrintStream(process.getErrorStream());
PrintStream inputStream = new PrintStream(process.getInputStream());
errorStream.start();
inputStream.start();
// 等待ffmpeg命令执行完
process.waitFor();
// 获取执行结果字符串
String result = errorStream.stringBuffer.append(inputStream.stringBuffer + "\n").toString();
// 输出执行的命令信息
if (showLog) {
logger.info("执行命令{}结果{}", cmd, result);
}
return result;
} catch (Exception e) {
logger.error("执行命令失败cmd{}失败:{} ", cmd, e.getMessage());
throw new BusinessException("视频转换失败");
} finally {
if (null != process) {
ProcessKiller ffmpegKiller = new ProcessKiller(process);
runtime.addShutdownHook(ffmpegKiller);
}
}
}
/**
* 在程序退出前结束已有的FFmpeg进程
*/
private static class ProcessKiller extends Thread {
private Process process;
public ProcessKiller(Process process) {
this.process = process;
}
@Override
public void run() {
this.process.destroy();
}
}
/**
* 用于取出ffmpeg线程执行过程中产生的各种输出和错误流的信息
*/
static class PrintStream extends Thread {
InputStream inputStream = null;
BufferedReader bufferedReader = null;
StringBuffer stringBuffer = new StringBuffer();
public PrintStream(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void run() {
try {
if (null == inputStream) {
return;
}
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
} catch (Exception e) {
logger.error("读取输入流出错了!错误信息:" + e.getMessage());
} finally {
try {
if (null != bufferedReader) {
bufferedReader.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
logger.error("调用PrintStream读取输出流后,关闭流时出错!");
}
}
}
}
}
以上是缩略图的创建以及图片的上传
以下是图片的展示:
@RequestMapping("/getResource")
public void getResource(HttpServletResponse response, @NotEmpty String sourceName) {
if (!StringTools.pathIsOk(sourceName)) {
throw new BusinessException(ResponseCodeEnum.CODE_600);
}
String suffix = StringTools.getFileSuffix(sourceName);
response.setContentType("image/" + suffix.replace(".", ""));
response.setHeader("Cache-Control", "max-age=2592000");
readFile(response, sourceName);
}
protected void readFile(HttpServletResponse response, String filePath) {
if (!StringTools.pathIsOk(filePath)) {
return;
}
File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + filePath);
if (!file.exists()) {
return;
}
try (OutputStream out = response.getOutputStream(); FileInputStream in = new FileInputStream(file)) {
byte[] byteData = new byte[1024];
int len = 0;
while ((len = in.read(byteData)) != -1) {
out.write(byteData, 0, len);
}
out.flush();
} catch (Exception e) {
log.error("读取文件异常", e);
}
}
两个接口的逻辑:
