前言
图片上传和预览在移动端应用非常广泛和频繁,vant组件库van-uploader组件已经帮我们实现了大部分功能,但是在系统中频繁使用还是有点麻烦,我们根据自身的业务系统重新封装了一下简化我们的开发。后端使用springboot集成jcifs实现文件管理微服务。
附件上传
附件预览
前端组件
组件介绍
前端只要在视图中使用组件,传入需要的参数即可
businessid | 业务id,用于把业务单号和附件关联 | tmp_id | 临时业务id,在一开始业务单号未产生的时候和附件关联 | name | 用于把不通类型的附件归类到不同的文件夹 | businesstype | 用于区分同一个业务单号下不通类型的附件组 | readonly | 判断组件是预览还是上传 |
<bdog-uploader-image
id="visit_uploader"
businessid="{{id}}"
tmp_id="{{tmp_id}}"
name="customerVisit"
businesstype="visit"
readonly ="{{readonly}}"
/>
组件js部分代码
const util = require('../../utils/util.js')
var request = require('../../utils/request.js')
var { config } = require('../../utils/config.js')
import Toast from '@vant/weapp/toast/toast';
const app = getApp();
Component({
properties: {
businessid: {
type: String
},
tmp_id: {
type: String
},
name: {
type: String
},
businesstype: {
type: String
},
readonly:{
type:Boolean
}
},
data: {
fileList: [],
},
attached:function(){
//this.getFileList()
},
methods: {
afterRead(event) {
Toast.loading({
duration: 0, // 持续展示 toast
forbidClick: true,
message: "上传中"
})
var that = this
const { file } = event.detail
wx.uploadFile({
url: config.baseUrl +'/MpAttachment/uploadFile',
filePath: file.url,
name: 'file',
header: {
"Content-Type": "multipart/form-data",
"id":that.data.businessid,
"tmpId":that.data.tmp_id,
"name":that.data.name,
"businesstype":that.data.businesstype,
"token":app.globalData.userInfo.token,
},
success(res) {
const data = JSON.parse(res.data)
if(data.code == 200){
// 上传完成需要更新 fileList
const { fileList = [] } = that.data;
const url = config.baseUrl +'/MpAttachment/getImage?id=' + data.data.id
fileList.push({ ...file, url: url, id: data.data.id })
that.setData({ fileList })
Toast.clear();
Toast({ type: 'success',message: '上传成功',duration:500, })
}else{
Toast({ type: 'fail',message: '上传失败',duration:500, })
}
},
fail:function(res){
Toast({ type: 'fail',message: '上传失败',duration:500, })
}
});
},
delete(event) {
Toast.loading({
duration: 0, // 持续展示 toast
forbidClick: true,
message: "删除中"
})
var that = this
var data = {}
data['id'] = event.detail.file.id
request.get('/MpAttachment/delete',data)
.then(function (res) {
if(res.code == 200){
const { fileList } = that.data;
const newFileList = fileList.filter((items) =>{
return items.id != event.detail.file.id
})
that.setData({ fileList : newFileList, })
Toast.clear();
Toast({ type: 'success',message: '删除成功',duration:500, })
}else{
Toast({ type: 'fail',message: '删除失败',duration:500, })
}
}, function (error) {
Toast({ type: 'fail',message: '删除失败',duration:500, })
})
},
getFileList() {
var that = this
var data = {}
data['businessid'] = that.data.businessid
data['businesstype'] = that.data.businesstype
request.get('/MpAttachment/getList',data)
.then(function (res) {
if(res.code == 200){
const fileList = res.data;
fileList.forEach(function(items){
items.url = config.baseUrl + '/MpAttachment/getImage?id=' + items.id
items.type = 'image'
})
that.setData({ fileList : fileList, })
}else{
Toast({ type: 'fail',message: '附件加载失败',duration:500, })
}
}, function (error) {
Toast({ type: 'fail',message: '附件加载失败',duration:500, })
})
}
}
})
组件视图部分代码
<van-cell title="" >
<van-uploader
slot="right-icon"
file-list="{{ fileList }}"
max-count="9"
bind:after-read="afterRead"
bind:delete="delete"
show-upload="{{ !readonly }}"
deletable="{{ !readonly }}"
/>
</van-cell>
<van-toast id="van-toast" />
后端微服务
后端微服务
微服务总归包含了附件上传、删除、获取图片、获取列表、附件上传个服务
?微服务代码
package com.brickdog.controller;
@RestController
@RequestMapping("/MpAttachment")
@Api(tags = { Swagger2Config.TAG_MpAttachment })
public class MpAttachmentController implements ServletContextAware {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected HttpSession session;
protected ServletContext servletContext;
String FileConnect ="/";
@Autowired
protected UserService userService;
@Autowired
@Qualifier("dispJdbcTemplate")
protected JdbcTemplate dispJdbcTemplate;
@Autowired
protected MpAttachmentService mpAttachmentService;
@ApiOperation(value = "获取列表", notes = "")
@GetMapping(value="/getList")
public Result getList(@ApiParam(value = "businessid" , required=true ) @RequestParam String businessid,
@ApiParam(value = "businesstype" , required=false ) @RequestParam String businesstype) throws ParseException {
List list = mpAttachmentService.getViewList(businessid,businesstype);
return Result.success(list,"成功!");
}
@CrossOrigin
@ApiOperation(value = "附件上传", notes = "")
@PostMapping("/uploadFile")
public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestHeader("name") String name, @RequestHeader String id, @RequestHeader String tmpId, @RequestHeader String businesstype) {
if (file.isEmpty()) {
return Result.failed("上传文件为空");
}
String uuid = UUID.randomUUID().toString();
// 获取文件名
String fileName = file.getOriginalFilename();
String newFileName = uuid + "."+ fileName.split("\\.")[1];
MpAttachment attachment = new MpAttachment();
attachment.setBusinessid(id);
attachment.setTmp_businessid(tmpId);
attachment.setBusinesstype(businesstype);
attachment.setFilename(fileName);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyMMdd");
String uploadPath = name + FileConnect + LocalDate.now().format(fmt);
attachment.setFilepath(uploadPath + FileConnect + newFileName);
try {
SmbFileUtils.save(file.getBytes(),uploadPath,newFileName);
attachment.setCreatetime(DateUtils.getNow());
attachment.setId(UUID.randomUUID().toString());
mpAttachmentService.add(attachment);
return Result.success(mpAttachmentService.getView(attachment.getId()),"成功!");
} catch (IOException e) {
e.printStackTrace();
return Result.failed("文件上传失败");
}
}
@CrossOrigin
@ApiOperation(value = "base64附件上传", notes = "")
@PostMapping("/base64UploadFile")
public Result base64UploadFile(@RequestBody String base64Image, @RequestHeader("fileName") String fileName, @RequestHeader("name") String name, @RequestHeader String id, @RequestHeader String tmpId, @RequestHeader String businesstype) throws UnsupportedEncodingException {
String uuid = UUID.randomUUID().toString();
base64Image = java.net.URLDecoder.decode(base64Image,"UTF-8");
fileName = java.net.URLDecoder.decode(fileName,"UTF-8");
id = java.net.URLDecoder.decode(id,"UTF-8");
String newFileName = uuid + "."+ fileName.split("\\.")[1];
MpAttachment attachment = new MpAttachment();
attachment.setBusinessid(id);
attachment.setTmp_businessid(tmpId);
attachment.setBusinesstype(businesstype);
attachment.setFilename(fileName);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyMMdd");
String uploadPath = name + FileConnect + LocalDate.now().format(fmt);
attachment.setFilepath(uploadPath + FileConnect + newFileName);
try {
byte[] imageByte = ImageUtils.base64ImageToByte(base64Image);
SmbFileUtils.save(imageByte,uploadPath,newFileName);
attachment.setCreatetime(DateUtils.getNow());
attachment.setId(UUID.randomUUID().toString());
mpAttachmentService.add(attachment);
return Result.success(mpAttachmentService.getView(attachment.getId()),"成功!");
} catch (Exception e) {
e.printStackTrace();
return Result.failed("文件上传失败");
}
}
@ApiOperation(value = "获取图片", notes = "")
@GetMapping(value="/getImage", produces = {MediaType.IMAGE_PNG_VALUE})
public BufferedImage getImage(@ApiParam(value = "id" , required=true ) @RequestParam String id) throws IOException {
MpAttachment attachment = mpAttachmentService.get(id);
if(attachment !=null)
{
InputStream imageInputStream = SmbFileUtils.getFile(attachment.getFilepath());
return ImageIO.read(imageInputStream);
}
return null;
}
@ApiOperation(value = "删除", notes = "")
@GetMapping(value="/delete")
public Result delete(@ApiParam(value = "id" , required=true ) @RequestParam String id) {
MpAttachment attachment = mpAttachmentService.get(id);
try {
SmbFileUtils.delete(attachment.getFilepath());
int result = mpAttachmentService.delete(id);
if(result >0){
return Result.success(attachment,"删除成功!");
}else {
return Result.success(attachment,"删除失败!");
}
} catch (Exception e) {
e.printStackTrace();
return Result.failed("失败");
}
}
@ModelAttribute
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response){
this.request = request;
this.response = response;
this.session = request.getSession();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
jcifs文件管理帮助类
package com.brickdog.common.utils;
import jcifs.CIFSContext;
import jcifs.CIFSException;
import jcifs.context.SingletonContext;
import jcifs.smb.*;
import java.io.*;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
public class SmbFileUtils {
static String ip = "127.0.0.1";
static String domain = "127.0.0.1/upload";
static String userName = "admin";
static String password = "admin";
static void SmbFileUtils(){
}
//根据账号密码登录
private static CIFSContext withNTLMCredentials(CIFSContext ctx) {
return ctx.withCredentials(new NtlmPasswordAuthenticator(domain,
userName, password));
}
//保存文件
public static String save(byte[] byteArr, String url,String fileName) throws IOException {
InputStream in = new ByteArrayInputStream(byteArr);
String status = "";
try {
CIFSContext context = withNTLMCredentials(SingletonContext.getInstance());
SmbFileWriter.createDirectory("smb://" + domain +"/" + url, context);
boolean result = SmbFileWriter.writeSmbFile(in, "smb://" + domain +"/" + url +"/" + fileName, context);
status = "success";
} catch (Exception e) {
e.printStackTrace();
status = "error";
} finally {
in.close();
return status;
}
}
//获取文件
public static InputStream getFile(String filePath) throws IOException {
String url = "smb://" + domain + "/" + filePath;
try {
CIFSContext context = withNTLMCredentials(SingletonContext.getInstance());
SmbFileReader reader = new SmbFileReader();
SmbFile file = reader.readSmbFile(url, context);
if (file !=null) {
return file.getInputStream();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//删除文件
public static String delete(String filePath) throws IOException {
String status = "";
String url = "smb://" + domain + "/" + filePath;
try {
CIFSContext context = withNTLMCredentials(SingletonContext.getInstance());
SmbFile file = new SmbFile(url, context);
if (file.exists()) {
file.delete();
status = "success";
}
} catch (Exception e) {
e.printStackTrace();
status = "error";
}
return status;
}
static class SmbFileReader {
public SmbFile readSmbFile(String path, CIFSContext context) throws IOException {
try (SmbFile file = new SmbFile(path, context)) {
if (Objects.isNull(file) || !file.exists()) {
throw new FileNotFoundException(path);
}
return file;
}
}
}
static class SmbFileWriter {
static boolean writeSmbFile(String source, String target, CIFSContext context) throws IOException {
if (StrUtils.isEmpty(source) || StrUtils.isEmpty(target)) {
return false;
}
return writeSmbFile(Files.newInputStream(Paths.get(source)),
target, context);
}
static boolean writeSmbFile(InputStream in, String target, CIFSContext context) throws IOException {
if (Objects.nonNull(in) && StrUtils.isNotEmpty(target)) {
try (SmbFile file = new SmbFile(target, context)) {
try (SmbFile parent = new SmbFile(file.getParent(), context)) {
if (!parent.exists()) {
createDirectory(file.getParent(), context);
}
if (!file.exists()) {
file.createNewFile();
}
}
try (OutputStream os = file.getOutputStream()) {
byte[] bytes = new byte[1024];
while (in.read(bytes) != -1) {
os.write(bytes);
}
return true;
}
}
}
return false;
}
static SmbFile createDirectory(String targetDir, CIFSContext context) throws MalformedURLException,
CIFSException, MalformedURLException {
try (SmbFile dir = new SmbFile(targetDir, context)) {
if (!dir.exists()) {
dir.mkdir();
}
return dir;
}
}
}
}
pom文件
这边jcifs包我们一定要使用2.0以上的,2.0以下经常会出现网盘权限认证卡住导致读取或者上传附件特别慢
<dependency>
<groupId>eu.agno3.jcifs</groupId>
<artifactId>jcifs-ng</artifactId>
<version>2.1.3</version>
</dependency>
生成文件
我们对每类文件进行文件加归类,每天一个文件夹分开存放附件
?表结构
?参考文献
https://github.com/codelibs/jcifs https://github.com/xuanyiying/jcifs-ng-smb2-demo
|