前言

图片上传和预览在移动端应用非常广泛和频繁,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, // 持续展示 toastforbidClick: true,message: "上传中"})var that = thisconst { file } = event.detailwx.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){// 上传完成需要更新 fileListconst { fileList = [] } = that.data;const url = config.baseUrl +'/MpAttachment/getImage?id=' + data.data.idfileList.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, // 持续展示 toastforbidClick: true,message: "删除中"})var that = thisvar data = {}data['id'] = event.detail.file.idrequest.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 = thisvar data = {}data['businessid'] = that.data.businessiddata['businesstype'] = that.data.businesstyperequest.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.iditems.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-uploaderslot="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" />

后端微服务

后端微服务

微服务总归包含了附件上传、删除、获取图片、获取列表、附件上传个服务

​​​​​​​

微服务代码

@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 ="/";@Autowiredprotected UserService userService;@Autowired@Qualifier("dispJdbcTemplate")protected JdbcTemplate dispJdbcTemplate;@Autowiredprotected 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 = "附件上传并添加水印", notes = "")@PostMapping("/uploadImageFile")public Result uploadImageFile(@RequestParam("file") MultipartFile file, @RequestHeader("name") String name, @RequestHeader String id, @RequestHeader String tmpId, @RequestHeader String businesstype) {User user = userService.findCueernt();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 {//添加水印InputStream input = new ByteArrayInputStream((file.getBytes()));/**给图片添加文字水印**/ArrayList<String> watermarkList =new ArrayList<String>();watermarkList.add("现场拍照[客户照片]");watermarkList.add(user.getName() +" " + DateUtils.dateToStr(new Date(),"yyyy-MM-dd HH:mm"));InputStream output = ImageWatermarkUtils.markImageByText(watermarkList,input,fileName.split("\\.")[1]);//文件上传SmbFileUtils.save(FileUtils.StreamToByte(output),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("失败");}}@ModelAttributepublic void setReqAndRes(HttpServletRequest request, HttpServletResponse response){this.request = request;this.response = response;this.session = request.getSession();}@Overridepublic 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();byte[] byteArr = reader.readSmbFile(url, context);InputStream input = new ByteArrayInputStream(byteArr);return  input;} 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 byte[] readSmbFile(String path, CIFSContext context) throws IOException {try  {SmbFile smbFile = new SmbFile(path, context);long fileSize = smbFile.length();if (fileSize > Integer.MAX_VALUE) {System.out.println("file too big...");return null;}InputStream fi = smbFile.getInputStream();byte[] buffer = new byte[(int) fileSize];int offset = 0;int numRead = 0;while (offset < buffer.length&& (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {offset += numRead;}// 确保所有数据均被读取if (offset != buffer.length) {throw new IOException("Could not completely read file "+ smbFile.getName());}fi.close();return buffer;} catch (Exception e) {e.printStackTrace();}return null;}}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;}}finally {in.close();}}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>

生成文件

我们对每类文件进行文件加归类,每天一个文件夹分开存放附件

表结构

添加水印

使用案列

//添加水印
InputStream input = new ByteArrayInputStream((file.getBytes()));
/**给图片添加文字水印**/
ArrayList<String> watermarkList =new ArrayList<String>();
watermarkList.add("现场拍照[客户照片]");
watermarkList.add(user.getName() +" " + DateUtils.dateToStr(new Date(),"yyyy-MM-dd HH:mm"));
InputStream output = ImageWatermarkUtils.markImageByText(watermarkList,input,fileName.split("\\.")[1]);
//文件上传
SmbFileUtils.save(FileUtils.StreamToByte(output),uploadPath,newFileName);

图片帮助类

package com.brickdog.common.utils;import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;/*** 图片添加水印工具类* 文字水印 图片水印 利用jdk ,不依赖第三方*/
public class ImageWatermarkUtils {static final String NEW_IMAGE_NAME_PRE_STR = "_water";// 水印透明度private static float alpha = 0.5f;// 水印文字字体private static Font font = new Font("宋体", Font.BOLD, 12);// 水印文字颜色private static Color color = Color.white;/*** 给图片添加水印、可设置水印图片旋转角度** @param iconPath   水印图片路径* @param srcImgPath 源图片路径* @param targerPath 目标图片路径* @param degree     水印图片旋转角度*/public static void markImageByIcon(String iconPath, String srcImgPath, String targerPath, Integer degree) {OutputStream os = null;try {if (StrUtils.isBlank(targerPath)) {targerPath = srcImgPath.substring(0, srcImgPath.lastIndexOf(".")) + NEW_IMAGE_NAME_PRE_STR + srcImgPath.substring(srcImgPath.lastIndexOf("."));}Image srcImg = ImageIO.read(new File(srcImgPath));BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);// 得到画笔对象// Graphics g= buffImg.getGraphics();Graphics2D g = buffImg.createGraphics();// 设置对线段的锯齿状边缘处理g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);if (null != degree) {// 设置水印旋转g.rotate(Math.toRadians(degree),(double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);}// 水印图象的路径 水印一般为gif或者png的,这样可设置透明度ImageIcon imgIcon = new ImageIcon(iconPath);// 得到Image对象。Image img = imgIcon.getImage();float alpha = 1f; // 透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));/*** 以下一算水印图位置,右下角*/int width = srcImg.getWidth(null);int height = srcImg.getHeight(null);int iconWidth = img.getWidth(null);int iconHeight = img.getHeight(null);int x = width - iconWidth;int y = height - iconHeight;x = x < 0 ? 0 : x;y = y < 0 ? 0 : y;// 表示水印图片的位置g.drawImage(img, x, y, null);g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));g.dispose();os = new FileOutputStream(targerPath);// 生成图片ImageIO.write(buffImg, "JPG", os);} catch (Exception e) {e.printStackTrace();} finally {try {if (null != os)os.close();} catch (Exception e) {e.printStackTrace();}}}/*** 给图片添加水印文字、可设置水印文字的旋转角度**/public static InputStream markImageByText(ArrayList<String> watermarkList, InputStream imageInputStream,String formatName) {try {// 1、源图片Image srcImg = ImageIO.read(imageInputStream);int srcImgWidth = srcImg.getWidth(null);int srcImgHeight = srcImg.getHeight(null);BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);// 2、得到画笔对象Graphics2D g = buffImg.createGraphics();// 3、设置对线段的锯齿状边缘处理g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null),srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);// 4、设置黑色遮罩int rowHeight = 20;int padding = 6;int height = rowHeight * watermarkList.size() + padding;int x = padding;int y = srcImgHeight - height;g.setColor(Color.black);g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.3f));g.fillRect(0,y,srcImgWidth,height);// 5、设置水印文字颜色g.setColor(color);// 6、设置水印文字Fontg.setFont(font);// 7、设置水印文字透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1f));// 8、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)for(int i=0;i<watermarkList.size();i++){g.drawString(watermarkList.get(i), x, y + rowHeight);y =y+rowHeight;}// 9、释放资源g.dispose();// 10、生成图片ByteArrayOutputStream os = new ByteArrayOutputStream();ImageIO.write(buffImg, formatName, os);InputStream  outStream = new ByteArrayInputStream(os.toByteArray());return  outStream;} catch (Exception e) {e.printStackTrace();} finally {try {} catch (Exception e) {e.printStackTrace();}try {} catch (Exception e) {e.printStackTrace();}}return  null;}/*** 获取水印文字总长度** @param waterMarkContent*            水印的文字* @param g* @return 水印文字总长度*/private static int getWatermarkLength(String waterMarkContent, Graphics2D g) {return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());}}

参考文献

https://github.com/codelibs/jcifs
https://github.com/xuanyiying/jcifs-ng-smb2-demo

微信小程序基于vant和springboot实现附件上传和预览相关推荐

  1. 微信小程序之使用vant-3组件Uploader文件上传

    效果图: 微信小程序之使用vant-3组件Uploader文件上传 1.把vant的weapp包加进来 2.在app.json文件里面"usingComponents"加载进来才能 ...

  2. 微信小程序开发工具能正常请求后台数据,手机预览请求失败

    微信小程序开发工具能正常请求后台数据,手机预览请求失败 问题描述 微信开发者工具调试完想用手机预览效果,结果发现凡是后台请求都失败. 原因分析 可能的原因有三个: 微信开发者工具详情里的本地设置,没有 ...

  3. 微信小程序 - 超详细 “纯前端“ 将文件上传到阿里云 OSS,最新阿里云 OSS 直传音视频、图片、word、excel、ppt、office 文档(全程无需后端,前端文件直传阿里云oss服务器)

    前言 网上的教程乱七八糟却文件少代码(并且没注释),而且都已经很老了,对于新手来说真的无从下手. 本文站在新手小白的角度,实现微信小程序开发中,"前端直传" 上传文件到阿里云oss ...

  4. 微信小程序多张图片和表单一起上传,验证表单及进度条的实现完整代码

    微信小程序开发交流qq群   173683895    承接微信小程序开发.扫码加微信. 正文: 效果图: 完整代码: <!--pages/register/register.wxml--> ...

  5. 【微信小程序】图片选择、转码、压缩、预览、上传,file与base64

    0 前言 微信小程序中,上传图片,如标题,涉及图片选择.图片转码.图片压缩.图片预览.图片上传等 1 图片选择 需要使用微信提供的wx.chooseMedia chooseImage() {var t ...

  6. 微信小程序实现录音格式为mp3,并上传到云开发环境

    前言 微信小程序中可以实现录音的主要有wx.startRecord()和wx.getRecorderManager(),其中wx.startRecord()从微信开发者工具基础库1.6后停止维护,开始 ...

  7. C# 做微信小程序后台,基本服务器配置 以及 图片上传

    最近参加腾讯的高校微信小程序大赛,用C#做过几个网站,便用C#做后台写接口,在操作的过程中遇到了很多问题.由于C#的生态(C#是最好的语言,不接受反驳),网上资料很少,一边摸索着一边写,还是很艰难的, ...

  8. 《微信小程序案例6》点击图片上传,从本地相册选择或打开摄像头拍摄上传照片

    点击一下相机图片实现上传本都相册图片或者打开摄像头拍照上传 使用微信小程序API里面的wx.chooseImage 实现点击并上传图片后把上传的图片放入上面的虚线框里面. 方法: 在wx.choose ...

  9. 微信小程序之图片、音频、视频上传(附前后端代码示例)

    最近搞小程序开发,碰到了图片上传和音视频上传的问题,百度了下,网上示例不多,鄙人不才,就用TP3.2.3自己写了一个小程序上传图片.音视频的示例,刚好也是项目所需.能力有限,不喜勿喷,仅供新手参考. ...

最新文章

  1. Java 命名规范(非常全)
  2. Android 全屏显示
  3. 大数模板(加减乘除幂次开方)
  4. 【在线画流程图】网站
  5. shell 工具_shell 脚本加密工具 - shc
  6. python循环套循环_零基础学python 14 循环套循环:循环的嵌套
  7. 在资源使用状况视图中查看资源的负荷情况
  8. Duplicate property mapping of contactPhone found in
  9. flann中关于数据的stride
  10. 读取P12格式证书的密钥
  11. C++调用V8与JS交互
  12. Android手机应用开发(一) | 基本的UI界面设计
  13. VO、DTO、BO、PO、DO、POJO 数据模型的理解和实际使用
  14. 微信自定义链接分享网页(设置标题+简介+图片)
  15. 神策应用-概述认知(一)
  16. Mbps、Kbps、bps、MB、KB、B、b的区别
  17. matlab边坡可靠性分析,滇西南地区高速公路高边坡调查及抗震稳定性分析
  18. java课设小迷宫含代码_Java小项目之迷宫游戏的实现方法
  19. python抠图太模糊_【图】为什么抠图后像素变差?解决在线抠图分辨率太低的办法...
  20. 批处理和流处理如何区分? 有边界数据和无边界数据如何区分?

热门文章

  1. java项目甘特图打印
  2. Unity 的用UnityEvent来自定义触发方式
  3. java 打成war_JAVA语言之Springboot打成war包并在tomcat中运行的部署方法
  4. 形态学重建:孔洞填充的python实现
  5. vue根据后端菜单数据生成动态路由
  6. Discuz!论坛教程之如何禁止/允许用户编辑自己发布的帖子
  7. /var/jenkins_home/workspace/demo_pipeline/env@tmp/durable-d9b28a5c/script.sh: /root/maven/apache-mav
  8. a股用计算机模拟走势,A股竟用此走势将空方一网打尽
  9. (一)、Spring Security OAuth2 五种授权方式介绍
  10. java中变量私有化