在做商品管理模块时,需要实现一个图片上传的功能,用作商品图片的上传。在后续的计划中,商品的图片需要生成缩略图用于展示。基于这个原因,前端采用了element-ui的upload组件的照片墙模式,这种模式有小图预览功能。后端采用了thumbnailator的组件,可支持一些图片的处理,比如缩放、旋转、裁剪之类的功能,正好保存两张图片,一张原图,一张经过处理之后的图片。
前端代码:
<el-upload
ref="imageUpload"
action="/attachmentInfo/uploadImageFile"
name="fileNames"
list-type="picture-card"
multiple
accept="image/png,image/jpg,image/jpeg"
:file-list="previewList"
:on-preview="handlePictureCardPreview"
:auto-upload="false"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:on-success="uploadSuccess"
:before-upload="beforeUpload"
:http-request="uploadFile"
>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过10MB</div>
<i class="el-icon-plus"></i>
</el-upload>
validateData() {
let validFlag = false
// 获取待上传的文件列表
const uploadFiles = this.$refs.imageUpload.uploadFiles
// 提交校验
this.$refs.commodityInfo.validate(valid => {
if (valid) {
validFlag = valid
}
if (uploadFiles && uploadFiles.length) {
uploadFiles.forEach((item, index) => {
if (validFlag && item.size > 10 * 1024 * 1024) {
validFlag = false
this.$message.error('第' + (index + 1) + '张图片大小不可超过10MB')
}
})
}
})
return validFlag
},
saveCommodityData() {
const _this = this
const saveDataFun = (callback) => {
let imageIdArray = []
// console.log(_this.previewList)
// console.log(_this.uploadFileList)
if (_this.previewList && _this.previewList.length) {
_this.previewList.forEach(item => imageIdArray.push(item.id))
}
// 获取待上传的文件列表
const uploadFiles = _this.$refs.imageUpload.uploadFiles
// 获取到新添加的文件
let newUploadFiles = uploadFiles.filter(item => item.response)
newUploadFiles.forEach((item2, index) => {
item2.index = index
item2 = {...item2, ...item2.response.data[index]}
newUploadFiles[index] = item2
imageIdArray.push(item2.id)
})
// console.log(newUploadFiles)
// 设置图片ids
_this.commodityInfo.commodityImgIds = imageIdArray.join(',')
// 设置商品分类名称
const commodityTypeIndex = _this.commodityTypeList.findIndex(item => String(item.id) === String(_this.commodityInfo.commodityTypeId))
_this.commodityInfo.commodityTypeName = commodityTypeIndex !== -1 ? _this.commodityTypeList[commodityTypeIndex].typeName : ''
// 设置商品信息
_this.commodityInfoSaveDTO.commodityInfo = JSON.parse(JSON.stringify(_this.commodityInfo))
// 设置商品明细数据
_this.commodityInfoSaveDTO.commodityDetailRecordList = JSON.parse(JSON.stringify(_this.detailRecord))
// 设置图片列表
_this.commodityInfoSaveDTO.imageList = _this.commodityInfoSaveDTO.imageList && _this.commodityInfoSaveDTO.imageList.length ? _this.commodityInfoSaveDTO.imageList : []
$.ajax({
url: getContext() + "/commodityInfo/saveCommodityInfo",
type: "POST",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(_this.commodityInfoSaveDTO),
success: function (data) {
if (data) {
if (data.isSuccess) {
window.top.ZXW_VUE.$notify.success({
message: '保存成功',
duration: '1000'
})
// 关闭窗口
_this.closeDialog()
} else {
window.top.ZXW_VUE.$notify.error({
message: data.msg,
duration: '1000'
})
}
}
},
error: function (msg) {
}
});
if (callback) {
callback()
}
}
if (this.validateData()) {
// 获取待上传的文件列表
const uploadFiles = this.$refs.imageUpload.uploadFiles
if (uploadFiles && uploadFiles.length) {
this.fileData = new FormData(); // new formData对象
// 调用上传方法
this.$refs.imageUpload.submit()
this.uploadFileList.forEach((file) => {// 因为要上传多个文件,所以需要遍历
this.fileData.append('fileNames', file.file)
})
// 设置额外参数
this.fileData.append("attachmentType", "commodityImg")
$.ajax({
url: getContext() + "/attachmentInfo/uploadImageFile",
type: "POST",
contentType: false,
processData: false,
data: this.fileData,
success: function (data) {
if (data.code === 0) {
_this.uploadFileList.forEach(item => {
item.onSuccess(data)
})
// 调用保存数据方法
saveDataFun(() => {
// 清空待上传文件列表
_this.uploadFileList = []
})
}
},
error: function (msg) {
_this.fileObj.onError(data)
}
});
} else {
// 调用保存数据方法
saveDataFun()
}
}
},
uploadFile(file) {
this.uploadFileList.push(file);
},
closeDialog() {
// 关闭弹窗
window.top.ZXW_VUE.closeFirstDialog()
},
handlePictureCardPreview(file) {
// 预览图片
this.dialogImageUrl = file.url;
window.top.ZXW_VUE.$alert('<img style="width: 60vw;height: 70vh" src="' + this.dialogImageUrl + '" alt="">', '预览图', {
dangerouslyUseHTMLString: true,
customClass: 'commodityInfoClass',
showConfirmButton: false
});
},
// 图片上传尺寸大小检验
beforeUpload(file) {
let _this = this
const isOver10M = file.size / 1024 / 1024 > 10; // 限制小于10M
if (isOver10M) {
_this.$message.error('图片大小不可超过10MB')
}
return !isOver10M
},
beforeRemove(file, fileList) {
const index = fileList.findIndex(item => item.uid === file.uid)
// 移除前
return window.top.ZXW_VUE.$confirm(`确定移除第${index + 1}张图片, ${file.name}吗?`).then(() => {
})
}解析:
action是请求地址,默认单图上传时是必须的,name属性的值是对应上传接口中的multipartFile对应的名称,同样是单图上传时需要的值。multiple值表示当前支持多选。accept表示支持的格式。file-list是对应回显的图片列表数据。auto-upload是自动上传属性。http-request覆盖默认上传的功能。
由于elementui的upload组件是默认自动上传的,且为一张一张上传,而我们实际的需求是多张图片选择完成之后,点击保存时再一并上传,上传之前进行校验文件大小,删除时进行提醒,所以就需要做一些特殊的处理。
1、关闭自动上传属性。
2、开启multiple多选属性。
3、http-request对应uploadFile方法,将file对象添加到缓存集合中
4、添加beforeRemove函数,提醒图片删除
5、在保存提交方法中,根据_this.$refs.imageUpload.uploadFiles验证上传图片的大小
6、创建FormData对象,再调用this.$refs.imageUpload.submit(),再从缓存对象中遍历数据,使用append方法添加到formData对象中,key为fileNames
7、通过append方法,添加其他参数
8、调用ajax上传请求,设置contentType: false,processData: false属性
后端代码:
pom依赖 <!-- 图片缩略图 --> <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.8</version> </dependency>
上传代码:
/**
* 上传图片附件
*
* @return 新增结果
*/
@ApiOperation(value = "上传图片附件", notes = "上传图片附件")
@PostMapping("/uploadImageFile")
public R<List<AttachmentInfo>> uploadImageFile(@RequestParam("fileNames") MultipartFile[] files,@RequestParam("attachmentType") String attachmentType) {
List<AttachmentInfo> attachmentInfoList = attachmentInfoService.uploadImageFile(files,attachmentType);
return success(attachmentInfoList);
}
public class AttachmentInfoServiceImpl extends ServiceImpl<AttachmentInfoMapper, AttachmentInfo> implements AttachmentInfoService {
@Value("${com.zjh.uploadPath}")
private String uploadPath;
@Override
public List<AttachmentInfo> uploadImageFile(MultipartFile[] files, String attachmentType) {
// 图片路径
String imageFilePath = uploadPath + "attachment" + File.separator + "images";
File path = new File(imageFilePath);
if (!path.exists()) {
path.mkdirs();
}
List<AttachmentInfo> attachmentInfoList = new ArrayList<AttachmentInfo>();
//判断file数组不能为空并且长度大于0
if (files != null && files.length > 0) {
//循环获取file数组中得文件
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
//保存文件
if (!file.isEmpty()) {
AttachmentInfo attachmentInfo = uploadSingleImageFile(file, attachmentType, imageFilePath);
attachmentInfoList.add(attachmentInfo);
}
}
}
// 批量保存附件
this.saveBatch(attachmentInfoList);
return attachmentInfoList;
}
@Override
public void removeImageByIds(List<Integer> needRemoveFileIds) {
// 根据id查询附件数据
List<AttachmentInfo> attachmentInfoList = this.list(Wrappers.<AttachmentInfo>lambdaQuery().in(AttachmentInfo::getId, needRemoveFileIds));
if (CollectionUtils.isNotEmpty(attachmentInfoList)) {
for (AttachmentInfo attachmentInfo : attachmentInfoList) {
// 获取文件路径
String fileUrl = attachmentInfo.getFileUrl();
// 拼接真实路径
String filePath = uploadPath + fileUrl;
// 调用删除
File file = new File(filePath);
if (file.isFile() && file.exists()) {
file.delete();
}
// 获取缩略图路径
String thumbnailUrl = attachmentInfo.getThumbnailUrl();
if (StringUtils.isNotBlank(thumbnailUrl)) {
// 拼接真实路径
String filePath1 = uploadPath + thumbnailUrl;
// 调用删除
File file1 = new File(filePath1);
if (file1.isFile() && file1.exists()) {
file1.delete();
}
}
}
// 批量删除附件
this.removeByIds(needRemoveFileIds);
}
}
public AttachmentInfo uploadSingleImageFile(MultipartFile imageFile, String attachmentType, String imageFilePath) {
// 创建附件对象
AttachmentInfo attachmentInfo = new AttachmentInfo();
attachmentInfo.setAttachmentType(attachmentType);
attachmentInfo.setDeleteBit(false);
String fileDirectory = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String uuid = UUID.randomUUID().toString();
//拼接后台文件名称
String pathName = fileDirectory + File.separator + uuid + "."
+ FilenameUtils.getExtension(imageFile.getOriginalFilename());
//拼接文件路径
String filePathName = imageFilePath + File.separator + pathName;
//判断文件保存是否存在
File file = new File(filePathName);
if (file.getParentFile() != null || !file.getParentFile().isDirectory()) {
//创建文件
file.getParentFile().mkdirs();
}
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
try {
inputStream = imageFile.getInputStream();
fileOutputStream = new FileOutputStream(file);
//写出文件
byte[] buffer = new byte[2048];
IOUtils.copyLarge(inputStream, fileOutputStream, buffer);
buffer = null;
} catch (IOException e) {
filePathName = null;
throw new BusinessException("操作失败" + e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
} catch (IOException e) {
filePathName = null;
throw new BusinessException("操作失败" + e.getMessage());
}
}
//拼接后台文件名称
String thumbnailPathName = fileDirectory + File.separator + uuid + "small."
+ FilenameUtils.getExtension(imageFile.getOriginalFilename());
if (thumbnailPathName.contains(".png")) {
thumbnailPathName = thumbnailPathName.replace(".png", ".jpg");
}
long size = imageFile.getSize();
/* double scale = 1.0d;
if (size >= 200 * 1024) {
if (size > 0) {
scale = (200 * 1024f) / size;
}
}
*/
//拼接文件路劲
String thumbnailFilePathName = imageFilePath + File.separator + thumbnailPathName;
try {
/* if (size < 200 * 1024) {
Thumbnails.of(filePathName).scale(1f).outputFormat("jpg").toFile(thumbnailFilePathName);
} else {
Thumbnails.of(filePathName).scale(1f).outputQuality(scale).outputFormat("jpg").toFile(thumbnailFilePathName);
}*/
Thumbnails.of(filePathName).size(400, 500).toFile(thumbnailFilePathName);
} catch (Exception e1) {
throw new BusinessException("操作失败" + e1.getMessage());
}
attachmentInfo.setFileName(imageFile.getOriginalFilename());
attachmentInfo.setFileSuffix(FilenameUtils.getExtension(imageFile.getOriginalFilename()));
attachmentInfo.setFileUrl("attachment" + File.separator + "images" + File.separator + pathName);
attachmentInfo.setThumbnailUrl("attachment" + File.separator + "images" + File.separator + thumbnailPathName);
return attachmentInfo;
}
}解析:
1、通过注解接收参数@RequestParam("fileNames") MultipartFile[] files,@RequestParam("attachmentType") String attachmentType,包括fileNames对应的多图文件,以及attachMenetType的额外参数
2、创建图片文件夹目录
3、调用图片上传方法
4、使用Thumbnails.of(filePathName).size(400, 500).toFile(thumbnailFilePathName);方法生成缩略图文件固定尺寸400*500
5、调用附件保存的接口,返回成功数据



还没有评论,来说两句吧...