图片编辑增加压缩功能

This commit is contained in:
1215525055@qq.com 2025-06-09 17:43:33 +08:00
parent 9a23c6442a
commit 6ffd5f01f4
5 changed files with 154 additions and 13 deletions

View File

@ -3,11 +3,17 @@ package cn.com.tenlion.aishop.controller.api.goods;
import cn.com.tenlion.aishop.pojo.dtos.goods.GoodsSimpleDTO;
import cn.com.tenlion.aishop.pojo.vos.goods.GoodsCheckVO;
import cn.com.tenlion.aishop.pojo.vos.goodslog.GoodslogVO;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import ink.wgink.annotation.CheckRequestBodyAnnotation;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.common.component.SecurityComponent;
import ink.wgink.exceptions.SaveException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.file.enums.UploadTypeEnum;
import ink.wgink.module.file.service.IDefaultFileService;
import ink.wgink.module.file.service.IFileService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.pos.FilePO;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResult;
import ink.wgink.pojo.result.SuccessResultData;
@ -15,13 +21,21 @@ import ink.wgink.pojo.result.SuccessResultList;
import cn.com.tenlion.aishop.pojo.dtos.goods.GoodsDTO;
import cn.com.tenlion.aishop.pojo.vos.goods.GoodsVO;
import cn.com.tenlion.aishop.service.goods.IGoodsService;
import ink.wgink.util.UUIDUtil;
import io.swagger.annotations.*;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* @ClassName: GoodsController
@ -39,6 +53,52 @@ public class GoodsController extends DefaultBaseController {
private SecurityComponent securityComponent;
@Autowired
private IGoodsService goodsService;
@Autowired
private IFileService iFileService;
@Autowired
private IDefaultFileService iFileService2;
@PostMapping({"upload-image"})
public SuccessResultData<String> uploadFile(@RequestParam("file") MultipartFile file, Double picturesThumbnails) throws IOException {
// 参数校验
if (file.isEmpty()) {
throw new SaveException("上传文件不能为空");
}
InputStream inputStream = file.getInputStream();
BufferedImage bufferedImage = ImageIO.read(inputStream);
if (bufferedImage == null) {
throw new SaveException("无效的图片文件");
}
BufferedImage image = bufferedImage;
if (picturesThumbnails != null) {
try {
image = Thumbnails.of(new BufferedImage[]{bufferedImage}).scale(1).outputQuality(picturesThumbnails / 100).outputFormat("jpg").asBufferedImage();
} catch (IOException e) {
throw new SaveException("图片压缩出现异常");
}
}
MultipartFile file1 = new MockMultipartFile(
"file", // 参数名表单中的name
UUIDUtil.get32UUID() + ".jpg", // 原始文件名
"image/jpeg",
convertToInputStream(image, "jpg") // 文件输入流
);
Map<String, Object> params1 = new HashMap<>();
String fileId1 = iFileService.uploadSingleByUserId(securityComponent.getCurrentUser().getUserId(), file1, UploadTypeEnum.IMAGE, params1);
return new SuccessResultData(fileId1);
}
public static InputStream convertToInputStream(BufferedImage image, String format) throws IOException {
// 1. 创建字节数组输出流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 2. 将BufferedImage写入输出流指定格式
if (!ImageIO.write(image, format, outputStream)) {
throw new IOException("Unsupported image format: " + format);
}
// 3. 转换为字节数组输入流
return new ByteArrayInputStream(outputStream.toByteArray());
}
@ApiOperation(value = "置顶", notes = "置顶接口")
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})

View File

@ -14,6 +14,7 @@ import cn.com.tenlion.freemarker.util.TemplatePageUtil;
import ink.wgink.annotation.CheckRequestBodyAnnotation;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.common.component.SecurityComponent;
import ink.wgink.exceptions.SaveException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.file.enums.UploadTypeEnum;
import ink.wgink.module.file.pojo.vos.FileVO;
@ -35,8 +36,10 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
@ -45,6 +48,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* @ClassName: OrderController
@ -71,6 +75,75 @@ public class OrderController extends DefaultBaseController {
private IFileService iFileService;
@Autowired
private IDefaultFileService iFileService2;
@ApiOperation(
value = "上传文件",
notes = "上传文件接口"
)
@ApiImplicitParams({@ApiImplicitParam(
name = "file",
value = "文件name",
paramType = "query"
)})
@ApiResponses({@ApiResponse(
code = 400,
message = "请求失败",
response = ErrorResult.class
)})
@PostMapping({"uploadfile"})
public SuccessResultData<String> uploadFile(@RequestParam("file") MultipartFile file) {
// 参数校验
if (file.isEmpty()) {
throw new SaveException("上传文件不能为空");
}
// 文件类型校验双重验证
String originalFilename = file.getOriginalFilename();
String contentType = file.getContentType();
if (originalFilename == null ||
(!originalFilename.toLowerCase().endsWith(".jpg") &&
!originalFilename.toLowerCase().endsWith(".jpeg"))) {
throw new SaveException("文件类型必须为jpeg或jpg");
}
if (contentType == null || !contentType.equalsIgnoreCase("image/jpeg")) {
throw new SaveException("文件类型必须为jpeg或jpg");
}
// 文件大小校验200KB
long maxSize = 200 * 1024;
if (file.getSize() > maxSize) {
throw new SaveException("文件大小不能超过200KB");
}
// 图片尺寸校验
try {
try (InputStream inputStream = file.getInputStream()) {
BufferedImage image = ImageIO.read(inputStream);
if (image == null) {
throw new SaveException("无效的图片文件");
}
int width = image.getWidth();
int height = image.getHeight();
if (width < 500 || width > 1500 || height < 500 || height > 1500) {
throw new SaveException( String.format("图片尺寸需在500×500至1500×1500之间当前尺寸%d×%d", width, height));
}
}
} catch (IOException e) {
throw new SaveException("图片读取失败:" + e.getMessage());
}
// 其他参数处理原代码中的params
Map<String, Object> params = this.requestParams();
String fileId = iFileService.uploadSingleByUserId(securityComponent.getCurrentUser().getUserId(),
file,
UploadTypeEnum.IMAGE,
params
);
return new SuccessResultData<>(fileId);
}
/**
* 读取到字节数组
* @param f 文件对象

View File

@ -49,9 +49,6 @@ public class GoodsonlineServiceImpl extends DefaultBaseService implements IGoods
@Override
public SuccessResultList<List<GoodsSimpleDTO>> listPageAll(ListPage page) {
if (page.getRows() > 12 ) {
page.setRows(12);
}
Map<String, Object> params = page.getParams();
if(params.get("goodsType") != null) {
params.put("goodsTypes", Arrays.asList(params.get("goodsType").toString().split(",")));

View File

@ -338,7 +338,7 @@
url: 'route/goods/image2?fileId='+ $('#goodsPhoto').val(),
title: '上传软著上架电子版',
width: '950px',
height: '600px',
height: '700px',
onClose: function() {
var uploadImage = localStorage.getItem('uploadImage');
console.log('uploadImage', uploadImage);

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" type="text/css" href="assets/js/vendor/cropper/cropper.min.css"/>
<style>
.upload-image {height: 100%; opacity: 0;}
.upload-image-preview {width: 200px;height: 200px;overflow: hidden;margin: 0 20px;display: inline-block;}
.upload-image-preview {width: 200px;height: 200px;overflow: hidden;margin: 0 44px;display: inline-block;}
#imageBox {height: 451px;overflow: auto;background-color: #F1F1F1;}
#imagePreviewBox .upload-image-preview {background-color: #F1F1F1;}
.cropper-container {
@ -50,7 +50,6 @@
<button type="button" class="btn btn-danger fa fa-refresh" onclick="reset()" title="重置图片"></button>
<button id="mosaicBtn" type="button" class="btn btn-warning fa fa-th" onclick="toggleMosaic()" title="马赛克"></button>
<button type="button" class="btn btn-info fa fa-reply" onclick="undoEdit()" title="撤销" id="undoBtn" disabled></button>
</div>
<div class="col-xs-4" style="margin-top: 5px; text-align: center;">
<div class="btn-group">
@ -81,7 +80,13 @@
<span id="mosaicSizeValue">10px</span>
</div>
</div>
<div class="col-xs-12" style="margin-top: 5px; text-align: center;">
<div class="form-group">
<label for="imageSize">图片压缩:</label>
<input type="range" id="imageSize" min="10" max="100" value="100" class="form-control">
<span id="imageSizeValue">100%</span>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="assets/js/jquery-3.5.1.min.js"></script>
@ -458,7 +463,7 @@
doUploadFile = function () {
var loadLayerIndex;
var formData = new FormData($('#uploadFileForm')[0]);
restAjax.postFile('api/file/uploadimage', formData, {}, function (code, data) {
restAjax.postFile('api/goods/upload-image', formData, {}, function (code, data) {
dialog.msg('上传成功');
cropper.replace('route/file/download/false/' + data.data, false);
localStorage.setItem('uploadImage', data.data);
@ -507,9 +512,10 @@
croppedCanvas.toBlob(function (cropBlob) {
var formData = new FormData();
formData.append("image", cropBlob);
formData.append("file", cropBlob);
formData.append("picturesThumbnails", $('#imageSize').val());
var loadLayerIndex;
restAjax.postFile('api/file/uploadimage', formData, {}, function (code, data) {
restAjax.postFile('api/goods/upload-image', formData, {}, function (code, data) {
dialog.msg('裁剪成功');
// 完全重置马赛克画布和历史记录
@ -567,6 +573,11 @@
$('#mosaicSizeValue').text(size + 'px');
});
$('#imageSize').on('input', function() {
var size = $(this).val();
$('#imageSizeValue').text(size + '%');
});
// 确保马赛克画布初始化
setTimeout(function() {
setupMosaicCanvas();