首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,590 阅读
2
Mac打印机设置黑白打印
4,903 阅读
3
修改elementUI中el-table树形结构图标
4,874 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,654 阅读
5
intelliJ Idea 2022.2.X破解
4,333 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
微信小程序
Oracle
Laughing
累计撰写
618
篇文章
累计收到
1,419
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
618
篇与
的结果
2024-05-30
ElementUI中一个页面多个el-descriptions列不对齐问题处理
使用ElementUI框架时,如果我们希望列表形式展示多个字段。可能经常使用Descriptions描述列表,如果一个页面只有一个Descriptions时,列宽显示是没有问题的,但是如果有多个Descriptions,你会发现,各个Descriptions之间的列宽,可能显示的不一致。为了保持各列宽度一致,我们可以设置contentStyle属性。<el-descriptions border :label-style="{ width: '110px' }" :contentStyle="content_style"> </el-descriptions> 然后在data里面,设置具体的content_stylecontent_style: { // 居左 'text-align': 'left', // 设置长度 width: '400px', // 排列第二行 'word-break': 'break-all' }
2024年05月30日
1,005 阅读
0 评论
1 点赞
2024-05-29
ElementUI中el-input-number实时监听值变化
在前端开发过程中,我们经常会遇到将金额转大写的情况,我们要实现的效果,是只要输入就实时计算大写,而不是等全部输入完成失去焦点后。如果使用ElementUI框架的el-input-number控件的@input事件,是无法达到实时显示效果的。html代码<el-col :span="24"> <el-form-item label="本次报价" prop="quotationPrice"> <el-input-number :precision="2" :step="100" :max="10000000" :min="0" v-model="form.quotationPrice" style="width: 100%" controls-position="right" data-unit="元" placeholder="请输入本次报价" ref="refQuotationPrice" @input.native="convertPrice2CnyMoney" @change="convertPrice2CnyMoney4Change" /> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="总金额" prop="cnyMoney"> <div class="cnyMoney">{{ cnyMoney }}</div> </el-form-item> </el-col> JavaScript代码//本次报价金额实时转大写 convertPrice2CnyMoney(event) { let value = this.$refs.refQuotationPrice.displayValue; this.cnyMoney = numToCny(value); }, //本次报价金额实时转大写 convertPrice2CnyMoney4Change(val) { this.cnyMoney = numToCny(val); }
2024年05月29日
838 阅读
0 评论
0 点赞
2024-05-12
MateBook X Pro 2020 升级Sanoma 14.4.1 EFI
先上截图我这次不是全新安装,是在BigSur的版本基础上,通过在线更新直接升级的。壹、电脑配置我这个是Matebook X Pro 2020最低配集成显卡那款,16G内存,其他信息不记得了,别的版本没有验证。贰、使用感受流畅度来讲,我个人感觉比BigSur流畅一丢丢。蓝牙:不知道是不是我之前BigSur的驱动有问题,机械师的蓝牙键盘没法用,升级最新的之后,机械师的蓝牙键盘能用了。苹果鼠标、键盘亲测能用,其他蓝牙鼠标,感觉跟之前一样不可用。叁、发现问题系统会偶发自动重启,使用两天,遇到过两回了,自动重启后没啥问题。附Sanoma EFI,天翼云盘,不限速下载https://cloud.189.cn/web/share?code=uIrmuayyiuYv(访问码:3whx){message type="warning" content="如果下载链接失效,烦请留言通知我,谢谢"/}
2024年05月12日
855 阅读
0 评论
0 点赞
2024-03-26
深度解析:如何在若依系统中集成阿里云OSS实现高效文件存储
零、引言随着信息化技术的快速发展,企业级应用对于海量文件存储的需求日益增长。而阿里云对象存储服务(OSS)以其高可用、高可靠、低成本的特点成为众多企业的首选解决方案。本文将以流行的开源后台管理系统——若依系统为例,详细阐述如何将其与阿里云OSS无缝集成,以实现文件资源的安全、高效存储。壹、若依系统上传文件的现状若依系统基于ElementUI的el-upload组件,对于我们的业务来讲,目前存在两个需要改进的地方(1)文件选择后会自动上传,这个在前面的文章有过介绍若依系统上传图片压缩 - 李森的博客 (llisen.cc)(2)若依系统上传文件是上传到应用服务器的,我们需要实现的是上传到阿里云OSS,同时可以将OSS内容,通过内网下载到ECS,方便备份文件,减少OSS存储费用。叁、开通并配置阿里云OSS首先,您需要在阿里云官网注册并登录账号,然后开通OSS服务。在控制台中创建一个新的Bucket,为您的项目设定专属的存储空间,并根据业务需求设置合适的访问权限和地域属性。获取Bucket的相关信息,包括Endpoint、AccessKey ID 和 AccessKey Secret,这是后续与OSS交互的重要凭证。肆、集成阿里云OSS SDK在若依系统的后端开发环境中,通过引入阿里云OSS SDK的依赖包:在根目录的pom.xml的properties配置阿里云OSS的版本 <properties> <aliyun-oss.version>3.17.4</aliyun-oss.version> </properties>在dependencyManagement配置阿里云 OSS依赖 <!--阿里云--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun-oss.version}</version> </dependency>接着,在项目的配置文件(若依是在admin工程的的resources文件夹中)中添加OSS相关的连接信息:Yaml# application.yml 示例 aliyun: endpoint: 'your-endpoint' endpointInternal: 'your-endpoint-internal' accessKeyId: 'your-access-key-id' accessKeySecret: 'your-access-key-secret' bucketName: 'your-bucket-name' urlPrefix: 'your-domain' urlPrefixInternal: 'https://' + 'your-endpoint-internal'解释一下上面几个配置的含义endpoint创建阿里云Bucket时提供的外网地域节点,使用这个endpoint实现文件的上传endpointInternal创建阿里云Bucket时提供的内网地域节点,为了节约费用,我们ECS跟OSS买的是同一个地域的,这样通过内网下载OSS的文件是不收取费用的,把文件通过内网备份到ECS后,我们可以在空闲的时候,将备份的文件,通过ECS下载到本地accessKeyId、accessKeySecret是您访问阿里云API的密钥,具有该账户完全的权限,这个可以在账户下的AccessKey管理查看bucketName这个是我们创建的Bucket名称伍、配置参数为了方便读取application.yml的配置参数,我们创建一个配置类并完成OSS初始化AliyunConfig.java@Configuration @ConfigurationProperties(prefix = "aliyun") @Data public class AliyunConfig { /** * 外网endpoint */ private String endpoint; /** * 内网endpoint */ private String endpointInternal; /** * key */ private String accessKeyId; /** * 密钥 */ private String accessKeySecret; /** * 空间名称 */ private String bucketName; /** * 外网Url前缀 */ private String urlPrefix; /** * 内网Url前缀 */ private String urlPrefixInternal; @Bean public OSS oSSClient() { return new OSSClient(endpoint, accessKeyId, accessKeySecret); } }陆、编写文件上传类第三步:编写文件上传逻辑在后端服务中创建一个专门处理文件上传的服务类或工具类,利用OSS SDK提供的API实现文件上传功能:AliyunFileUploadService.java@Component @Slf4j public class AliyunFileUploadService { /** * 默认大小 50M */ public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; @Resource private OSS ossClient; @Resource private AliyunConfig aliyunConfig; /** * 阿里云文件上传 * * @param file 上传的文件 * @param ownerDirectory 目录 */ public String upload(MultipartFile file, String ownerDirectory) throws InvalidExtensionException, IOException { //文件新路径 String originalFilename = file.getOriginalFilename(); // 校验格式、大小等 boolean isLegal = false; assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); String filePath = getFilePath(file, ownerDirectory); // 上传到阿里云 ossClient.putObject(aliyunConfig.getBucketName(), filePath, new ByteArrayInputStream(file.getBytes())); //this.aliyunConfig.getUrlPrefix() + filePath 文件路径需要保持数据库 return aliyunConfig.getUrlPrefix() + filePath; } /** * 生成文件路径 * * @param file 文件 * @param ownerDirectory 自定义目录 * @return 生成的文件目录 */ private String getFilePath(MultipartFile file, String ownerDirectory) { String fileName; String extension = getExtension(file); if (!StringUtils.isEmpty(ownerDirectory)) { fileName = ownerDirectory + "/" + IdUtils.fastUUID() + "." + extension; } else { fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; } return fileName; } /** * 查看文件列表 * * @return 对象信息 */ public List<OSSObjectSummary> list() { // 设置最大个数。 final int maxKeys = 200; // 列举文件。 ObjectListing objectListing = ossClient.listObjects(new ListObjectsRequest(aliyunConfig.getBucketName()).withMaxKeys(maxKeys)); List<OSSObjectSummary> sums = objectListing.getObjectSummaries(); return sums; } /** * 删除文件 * * @param objectName 文件名 * @return 结果 */ public boolean delete(String objectName) { //如果文件路径是OSS的,截取后删除,否则不处理,直接返回成功 if (objectName != null && objectName.contains(aliyunConfig.getUrlPrefix())) { objectName = objectName.replace(aliyunConfig.getUrlPrefix(), ""); ossClient.deleteObject(aliyunConfig.getBucketName(), objectName); return true; } return true; } /** * 下载文件下载文件 * * @param objectName 数据库存储的文件路径 */ public void exportOssFile(String objectName) throws IOException { // ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。 if (objectName != null && objectName.contains(aliyunConfig.getUrlPrefix())) { objectName = objectName.replace(aliyunConfig.getUrlPrefix(), ""); // 创建OSSClient实例。 OSS ossClientLocal = new OSSClientBuilder().build(aliyunConfig.getEndpointInternal(), aliyunConfig.getAccessKeyId(), aliyunConfig.getAccessKeySecret()); try { File file = getAbsoluteFile(objectName); ossClientLocal.getObject(new GetObjectRequest(aliyunConfig.getBucketName(), objectName), file); }catch (Exception exception){ throw new CustomException(exception.getMessage()); }finally { if (ossClientLocal != null) { ossClientLocal.shutdown(); } } } } /** * 文件大小校验 * * @param file 上传的文件 * @throws FileSizeLimitExceededException 如果超出最大大小 */ public void assertAllowed(MultipartFile file, String[] allowedExtension) throws FileSizeLimitExceededException, InvalidExtensionException { long size = file.getSize(); if (size > DEFAULT_MAX_SIZE) { throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); } String fileName = file.getOriginalFilename(); String extension = getExtension(file); if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, fileName); } else { throw new InvalidExtensionException(allowedExtension, extension, fileName); } } } /** * 获取文件名的后缀 * * @param file 表单文件 * @return 后缀名 */ public String getExtension(MultipartFile file) { String extension = FilenameUtils.getExtension(file.getOriginalFilename()); if (StringUtils.isEmpty(extension)) { extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); } return extension; } /** * 判断MIME类型是否是允许的MIME类型 * * @param extension 扩展名 * @param allowedExtension 允许的扩展名 * @return true-允许;false-不允许 */ public boolean isAllowedExtension(String extension, String[] allowedExtension) { for (String str : allowedExtension) { if (str.equalsIgnoreCase(extension)) { return true; } } return false; } /** * 生成本地文件 * * @param fileName 文件名 * @return 文件 * @throws IOException 异常 */ private File getAbsoluteFile(String fileName) throws IOException { String uploadDir = LeeFrameConfig.getProfile(); File desc = new File(uploadDir + File.separator + fileName); if (!desc.getParentFile().exists()) { desc.getParentFile().mkdirs(); } if (!desc.exists()) { desc.createNewFile(); } return desc; } }柒、Controller public AjaxResult add(List<MultipartFile> imageFileList, @RequestParam("form") String form) throws IOException, InvalidExtensionException { }Controller通过MultipartFile接收前端传递的文件,然后调用服务层完成上传。捌、前端交互在若依系统的前端部分,当用户选择文件后,前端需将文件转换为二进制数据并通过Ajax或者其他HTTP请求方式发送给后端。后端接收到请求后,调用OSS服务进行文件上传并将返回的URL反馈至前端展示或保存至数据库。具体可以参考若依系统上传图片压缩 - 李森的博客 (lisen.cc)其他、安全与优化考量为了增强安全性,可以考虑使用STS临时访问凭证进行上传操作,防止关键密钥泄露。另外,如果希望提高文件访问速度,可以为Bucket开启CDN加速,并根据实际场景调整缓存策略。总结起来,通过上述步骤,我们成功实现了若依系统与阿里云OSS的集成,使得整个系统的文件存储和管理能力得到了显著提升。这一过程不仅展示了云存储服务的优势,也展现了若依系统良好的扩展性和兼容性,为企业级应用提供了更加灵活且高效的文件管理方案。
2024年03月26日
1,867 阅读
2 评论
0 点赞
2024-03-26
阿里云OSS+CDN:一记组合拳,轻松降低您的建站成本
在数字化时代,拥有一个高效、稳定且成本优化的网站对于企业的线上业务至关重要。如果你遇到跟我类似的业务,需要上传大量图片的场景,可以参考一下我是如何降低费用的。在企业网站部署时,硬盘及带宽是相对比较费钱的地方,为了降低ECS带宽及硬盘费用,我们可以采用OSS的方案,前端将图片压缩后,存储到OSS中,但是即便如此,如果我们图片比较多的话,OSS的外网流出费用也是相对比较贵的,相对比而言,CDN的费用要相对便宜很多。为了尽可能的降低费用,我么可以采用阿里云对象存储服务(OSS)与内容分发网络(CDN)的组合运用的“降本增效”组合拳,降低建站成本,提升网站性能。本文将深入探讨如何借助阿里云OSS+CDN实现这一目标。壹、OSS:低成本、高可用的对象存储服务存储成本优化阿里云OSS作为一款大规模、安全可靠的云存储服务,其计费模式灵活,仅按实际使用量付费,无需预先投入高昂的硬件购置和运维成本。同时,OSS提供多种存储类型(如标准存储、低频访问存储、归档存储等),用户可根据数据访问频率和生命周期选择最经济的存储方案,进一步节省成本。高扩展性与可用性随着网站数据量的增长,OSS能够无缝扩展存储空间,无需担心容量瓶颈。其服务可用性高达99.999999999%(12个9),确保数据随时可访问。此外,OSS支持多版本控制、跨区域复制等功能,为数据安全与业务连续性提供有力保障。贰、CDN:全球加速,提升用户体验内容分发,缩短访问延迟CDN通过在全球范围内部署节点,将网站内容缓存至距离用户最近的服务器,大大减少了数据传输的距离和时间,显著降低访问延迟,提升用户浏览体验。尤其对于具有大量静态资源(如图片、视频、CSS、JS文件等)的网站,效果尤为明显。流量成本控制通过CDN分发,大部分用户请求直接由CDN节点响应,减轻了源站压力,降低了回源带宽费用。同时,阿里云CDN采用阶梯计费方式,随着使用量增大,单位流量成本逐渐降低,有效控制了大流量场景下的成本支出。叁、OSS+CDN:双剑合璧,降本增效简化网站架构,降低运维成本将网站静态资源托管于OSS,并配合CDN进行分发,可以简化网站架构,减少服务器负担,降低运维复杂度。用户只需专注于核心业务逻辑开发与运营,无需过多关注存储扩容、数据备份、服务器维护等繁琐工作,从而节省人力及时间成本。实时同步,保证内容一致性OSS与CDN深度集成,支持自动刷新、预热等机制,确保CDN节点上的内容与OSS中的原始数据保持实时同步,避免因数据更新不及时导致的用户体验下降。数据分析,助力精细化运营阿里云CDN提供详尽的访问日志和数据分析功能,帮助用户了解用户行为、地域分布、热门资源等信息,为网站优化、精准营销提供数据支持,进一步提升运营效率。总结来说,阿里云OSS与CDN的组合使用,不仅从存储成本、访问速度、运维复杂度等多个维度降低了建站成本,还提升了网站性能与用户体验,为企业的线上业务发展提供了强大支撑。在数字化转型的浪潮中,善用云服务工具,尤其是如阿里云OSS+CDN这样的“降本增效”组合拳,无疑是企业实现低成本、高效率建站的明智之选。肆、实战1. 创建对象存储OSS当我们使用OSS+CDN的组合方式时,我们购买资源包时,只需要购买两个(1)标准存储放,用于放我们上传的文件(2)CDN回源流量包,用于CDN回源流量。对于这两个资源包,在OSS中是相对比较便宜的,我们不需要在购买昂贵的外网流出费用资源包。我们不介绍如何使用OSS,本文假设你已经创建了一个Bucket找到【权限控制】→【读写权限】,将Bucket ACL改成私有,之所以改成私有,我是基于这三个考虑(1)我使用阿里云的CDN,不会影像CDN回源(2)防止有人通过默认的域名恶意刷流量(3)防止产生外网流出费用。找到【Bucket配置】→【域名管理】,点击绑定域名,绑定我们自己的域名。配置完域名后,我们点击阿里云CDN加速,会自动跳转到阿里云CDN配置界面2.CDN配置CDN配置中,主要是要注意回源配置在配置源站信息时,我们选择OSS域名,在域名中,会自动列出我们已经创建的Bucket,我们选择对应的Bucket还记得我们上面在配置Bucket是设置的ACL是私有的,所以在CDN的回源配置中,我们需要勾选阿里云OSS私有Bucket回源3.使用最后需要注意的一点就是,我们在使用图片是,需要通过我们绑定的二级域名,不能使用阿里云OSS默认的域名,如果你没有配置Bucket ACL为私有,那么使用阿里云默认域名访问时,会产生外网流出费用,如果配置了配置Bucket ACL为私有,那么阿里云默认的域名是不能访问的,这也是为什么我们配置Bucket ACL为私有的一个原因。
2024年03月26日
762 阅读
0 评论
0 点赞
2024-03-24
Spring Boot Controller调用某个方法报Service注入为null
最近为了部署方便,尝试将项目的依赖与配置文件分开进行打包,可以参考Spring Boot分开打包依赖及配置文件 - 李森的博客 (lisen.cc)项目部署之后,试了一下,没有报错,但是后面在用的时候,有一个接口始终报空指针,通过日志分析,是服务层没有注入导致的。接口通过@Resource注入的 @Resource private ICarQuotationPriceHistoryService carQuotationPriceHistoryService;首先,既然别的接口都不存在问题,那么可以断定出现问题不是我们分打开打包依赖导致的。其次,在Idea中直接运行时,接口也不报错,说明方法本身不存在问题(姑且这么说吧),检查了配置、包名等地方,都没有发现问题。既然问题出现在这个方法,那说明肯定是这个方法出现了问题,检查了方法的注解、参数等,也都没发现问题,就在检查方法属性的时候,突然发现问题了,这个方法没有public,其他方法都是有pubic的,方法加上public后,问题解决其实这个地方,只是粗心大意了,忘记写public了。我们都知道,当一个方法没有修饰符时,默认就是default,default通常称为默认访问模式。该模式下,只允许在同一个包中进行访问。这也就为什么我们在不拆分依赖的时候,接口能正常访问,当我们拆分依赖后,因为我们这个是一个单独的模块(依赖),这个接口就无法访问了。通过这件事,得到了两个教训:1.做事不可粗心大意,像controller的方法,记得加public修饰符。2.遇到事情不要被表象迷惑,比如这种注入是null的,我们一般首先想到的是包名、扫描配置、注解上出现问题,往往不会考虑方法修饰符出现问题了。
2024年03月24日
657 阅读
0 评论
0 点赞
2024-03-24
Spring Boot分开打包依赖及配置文件
壹、为何要分开打包依赖Spring Boot默认会将依赖全部打包到一个jar中,这样导致的问题就是我们的一个jar往往很大。加之平时我们分模块开发,往往修改很小的一个部分,就需要打包整个jar包,上传整个jar到服务器。比如我用阿里云服务器,3M的带宽,如果我不拆分开依赖,仅仅是上传jar都需要耗时接近1分钟的时间。当然这样也有一些其他问题,比如我这种多模块的项目,如果我们修改了其他模块(非启动类所在模块),那么我们需要记得将打包的jar要放到依赖对应的文件夹中。贰、为何要分开打包配置文件相对于分开打包依赖,其实配置文件才是更有必要打包的。Spring Boot配置文件默认包裹在jar包中的形式,一方面容易造成配置文件的覆盖,另一方面修改配置文件也相对比较麻烦。叁、如何拆分打包依赖及配置文件Spring Boot分开打包依赖及配置文件的方法也比较简单,我们只需要修改pom.xml文件即可。只需要注意一点就是,如果我们是多模块的项目,需要修改主工程的pom.xml文件。添加一些配置属性,方便修改 <properties> <!--依赖输出目录--> <output.dependence.file.path>../output/lib/</output.dependence.file.path> <!--manifest中lib配置路径--> <manifest.classpath.prefix>lib</manifest.classpath.prefix> <!--jar输出目录--> <output.jar.file.path>../output/</output.jar.file.path> <!--配置文件输出目录--> <output.resource.file.path>../output/config/</output.resource.file.path> </properties>我这里实现的效果是把所有的文件都放到项目顶级的output文件夹中,项目的jar放到output中,依赖放到lib文件夹中,配置文件放到config文件夹中然后我们修改打包插件 <build> <plugins> <!-- 打JAR包,不包含依赖文件;显式剔除配置文件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <outputDirectory>${output.jar.file.path}</outputDirectory> <!-- 将配置文件排除在jar包 --> <excludes> <exclude>*.properties</exclude> <exclude>*.yml</exclude> <exclude>*.xml</exclude> <exclude>*.txt</exclude> </excludes> <archive> <!-- 生成的jar中,包含pom.xml和pom.properties这两个文件 --> <addMavenDescriptor>true</addMavenDescriptor> <!-- 生成MANIFEST.MF的设置 --> <manifest> <!--这个属性特别关键,如果没有这个属性,有时候我们引用的包maven库 下面可能会有多个包,并且只有一个是正确的, 其余的可能是带时间戳的,此时会在classpath下面把那个带时间戳的给添加上去,然后我们 在依赖打包的时候, 打的是正确的,所以两头会对不上,报错。 --> <useUniqueVersions>false</useUniqueVersions> <!-- 为依赖包添加路径, 这些路径会写在MANIFEST文件的Class-Path下 --> <addClasspath>true</addClasspath> <!-- MANIFEST.MF 中 Class-Path 各个依赖加入前缀 --> <!--这个jar所依赖的jar包添加classPath的时候的前缀,需要 下面maven-dependency-plugin插件补充--> <!--一定要找对目录,否则jar找不到依赖lib--> <classpathPrefix>${manifest.classpath.prefix}</classpathPrefix> <!--指定jar启动入口类 --> <mainClass>cc.lisen.LeeFrameApplication</mainClass> </manifest> <manifestEntries> <!-- 假如这个项目可能要引入一些外部资源,但是你打包的时候并不想把 这些资源文件打进包里面,这个时候你必须在 这边额外指定一些这些资源文件的路径,假如你的pom文件里面配置了 <scope>system</scope>,就是你依赖是你本地的 资源,这个时候使用这个插件,classPath里面是不会添加,所以你得手动把这个依赖添加进这个地方 --> <!--MANIFEST.MF 中 Class-Path 加入自定义路径,多个路径用空格隔开 --> <!--此处resources文件夹的内容,需要maven-resources-plugin插件补充上--> <Class-Path>${output.resource.file.path}</Class-Path> </manifestEntries> </archive> </configuration> </plugin> <!-- 复制依赖的jar包到指定的文件夹里 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <!-- 拷贝项目依赖包到指定目录下 --> <outputDirectory>${output.dependence.file.path}</outputDirectory> <!-- 是否排除间接依赖,间接依赖也要拷贝 --> <excludeTransitive>false</excludeTransitive> <!-- 是否带上版本号 --> <stripVersion>false</stripVersion> </configuration> </execution> </executions> </plugin> <!-- 用于复制指定的文件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <executions> <!-- 复制配置文件 --> <execution> <id>copy-resources</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <resources> <resource> <directory>src/main/resources</directory> <includes> <!--将如下格式配置文件拷贝--> <exclude>*.properties</exclude> <exclude>*.yml</exclude> <exclude>*.xml</exclude> <exclude>*.txt</exclude> </includes> </resource> </resources> <!--输出路径--> <outputDirectory>${output.resource.file.path}</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> <finalName>${project.artifactId}</finalName> </build>
2024年03月24日
1,010 阅读
0 评论
0 点赞
2024-03-23
Typora配置又拍云图片并设置文件上传路径
之前使用Typora配置过七牛云的存储,但是对于我这种几乎没有任何收益的小站,任何支出都是多余的,之所以用又拍云,主要是加入又拍云联盟后,每月能免费获取10GB存储空间外加15GB的流量,而且与阿里云存储不同,它这个15G的流量是不限制HTTP或者HTTPS请求的。但是又拍云也有其劣势,作为小众存储服务提供商,整体来讲使用起来不是很方便,功能也比较简单,在网站上,基本就能查看一下已经上传的文件,没法直接下载上传的文件,这块对于备份来说比较麻烦,如果不会调用其SDK需要考虑借助别人开发的工具。壹、创建又拍云存储服务登录又拍云管网,找到存储服务,点击创建服务服务名称我们输入有意义的名称就可以了应用场景因为我是存储博客照片,所以选择网页照片,按需选择就行了储存类型我选择标准类型,如果存储不常适用的文件,也可以选择低频访问类型地区选择这个只能选择国内加速,国际加速好像要收费,这个我没试过授权管理员这个我们一会配置PicGo的时候会用到,没有的话创建一个,记住用户名及密码,后面会用到以上信息输入完成后,完成创建服务。服务创建后,我们可以继续完善我们的服务,配置绑定域名及HTTPS等,这个不再这里介绍了。贰、安装PicGo我这里没有命令行的方式,是因为我需要适用PicGo的插件。因为博客适用的Typecho,他默认的文件上传路径为usr/upload/年/月的格式,为了保持一致,我也希望上传到图床的文件,保持这么一个格式。PicGo安装完成后,打开界面,找到图床设置下拉到最下面,找到又拍云,点击默认的打开下面的界面图床配置名这个不用动,默认Default即可,我们其实一般也就配置一个设定Bucket这个不要被名称迷惑,其实就是我们创建服务时的服务名设定操作员这个就是我们创建服务时创建的操作员设定管理员密码这个是我们创建的操作员密码设定加速域名这个是我们服务绑定的域名设定存储路径这个是我们文件上传的路径配置完成后,我们可以上传个文件测试一下配置是否正确叁、配置PicGo插件上面我们也说了,我们希望配置文件上传路径为usr/upload/年/月的格式以便与Typecho保持一致。但是PicGo本身是不支持配置这种格式的路径的,好比我们上面配置的路径为typecho/uploads/,因此我们需要借助插件实现这种文件路径。我这里借助picgo-plugin-super-prefix实现这种效果打开PicGo,找到插件设置,搜索picgo-plugin-super-prefix并安装安装完成后,点击插件设置安装如下进行配置安装可以上传一个图片,查看是否正确按照配置的路径上传文件。肆、配置TyporaTypora的配置就比较简单了,打开Typora,找到偏好设置,打开后,切换到图像页签插入图片时选择上传图片上传服务选择PicGo.app配置完成后,可以点击【验证图片上传选项】测试一下图片是否能上传成功。
2024年03月23日
716 阅读
0 评论
1 点赞
2024-03-22
若依系统上传图片压缩
虽然标题里面有若依,实际上所有的Vue项目都能够适用的。最近基于若依系统做点东西,若依系统本身封装了一个图片上传组件ImageUpload,但是这个组件对我来说不太适用,最主要的问题就是这个组件是自动上传的,这样就会导致我们业务数据跟附件无法做到同步,比如我们新增一个单据,点了上传图片,此时图片已经上传到服务器,但是我并没有保存单据,这样就造成了部分垃圾数据。当然不是说不能处理,只是来说更加麻烦。先来说下我们目前做的功能需求吧:基于ElementUI的el-upload上传图片为了减少图片占用的空间(目前基于阿里云OSS,这个后期介绍),我们在前端上传图片之前在不影响图片展示质量的前提下,对图片进行压缩表单数据与图片文件同步上传可以单张图片上传也可以同时上传多张图片(以下我们以多张图片上传来说明)壹、提取图片压缩公共方法在ruoyi.js公共方法中,封装图片压缩方法/** 图片压缩,默认同比例压缩 * @param {Object} fileObj * 图片对象 * 回调函数有一个参数,base64的字符串数据 */ export function compress(fileObj, callback) { // console.log('压缩前文件大小', fileObj.size) try { const image = new Image() image.src = URL.createObjectURL(fileObj) image.onload = function () { const that = this // 默认按比例压缩 let w = that.width let h = that.height const scale = w / h w = fileObj.width || w h = fileObj.height || (w / scale) let quality = 0.5 // 默认图片质量为0.7 // 生成canvas const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') // 创建属性节点 const anw = document.createAttribute('width') anw.nodeValue = w const anh = document.createAttribute('height') anh.nodeValue = h canvas.setAttributeNode(anw) canvas.setAttributeNode(anh) ctx.drawImage(that, 0, 0, w, h) // 图像质量 if (fileObj.quality && fileObj.quality <= 1 && fileObj.quality > 0) { quality = fileObj.quality } // quality值越小,所绘制出的图像越模糊 const data = canvas.toDataURL('image/jpeg', quality) // 压缩完成执行回调 const newFile = convertBase64UrlToBlob(data) callback(newFile) // console.log('压缩后文件信息', newFile) } } catch (e) { console.log('压缩失败!') } } // Base64 => 二进制(Blob) function convertBase64UrlToBlob(urlData) { // 去掉url的头,并转换为byte const bytes = window.atob(urlData.split(',')[1]) // 处理异常,将ascii码小于0的转换为大于0 const ab = new ArrayBuffer(bytes.length) const ia = new Uint8Array(ab) for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i) } return new Blob([ab], {type: 'image/png'}) } 在main.js中挂载公共方法,方便在后面使用Vue.prototype.compress = compress贰、页面增加上传图片功能在表单中,增加上传图片功能<el-upload action="#" list-type="picture-card" :auto-upload="false" multiple :headers="headers" ref="carPhotoListRef" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :on-change="handleChange" :file-list="imageFileList" :disabled="form.inquiryStatus !== 'inquirying'" :class="{ hide: form.inquiryStatus !== 'inquirying' }" accept="image/bmp,image/jpg,image/png,image/svg,image/psd,image/webp,image/jpeg"> <i class="el-icon-plus"></i> </el-upload> <el-dialog :visible.sync="dialogImageVisible" :append-to-body="true"> <img width="100%" :src="dialogImageUrl" alt=""/> </el-dialog>因为我们要手工上传图片,所以需要对el-upload的属性进行配置action因为我们要收工上传,所以action需要设置成#auto-upload设置成false禁止自动上传headers是基于若依的认证,传递头部信息的on-preview预览图片on-remove删除图片同步处理我们后端绑定数据on-change图片改变的钩子,因为我们要手工上传图片,因此通过这个钩子完成图片的压缩其他的一些属性基本设置图片预览,限制上传图片类型的,按需设置即可。data里面主要设置图片预览、绑定图片列表等属性data() { return { headers: { Authorization: "Bearer " + getToken() }, //预览图片窗口是否可见 dialogImageVisible: false, //预览图片Url dialogImageUrl: null, imageFileList: [], } }method方法如下//删除图片 handleRemove(file, fileList) { this.imageFileList = fileList if (this.form.carPhotoList) { let index = -1 for (let i = 0; i < this.form.carPhotoList.length; i++) { if (this.form.carPhotoList[i].photoId === file.photoId) { index = i } } if (index !== -1) { this.$delete(this.form.carPhotoList, index) } } }, //图片改变 handleChange(file, fileList) { let that = this // 调用自定义的压缩方法 compress(file.raw, function (val) { // 图片格式: blob => file let newFile = new window.File([val], file.name, {type: file.raw.type}); // 新增属性(file)并赋值 let fileObj = {} fileObj.raw = newFile fileObj.name = file.name fileObj.uid = file.uid; fileObj.url = URL.createObjectURL(file.raw); that.imageFileList.push(fileObj) }) // this.imageFileList = fileList }, //预览图片 handlePictureCardPreview(file) { this.dialogImageUrl = file.url this.dialogImageVisible = true },实现图片压缩主要是通过on-change钩子,选择图片后,通过on-change钩子调用handleChange方法,我们拿到图片文件后,调用compress方法,通过回调函数,将压缩后的图片绑定到imageFileList表单保存/** 提交按钮 */ submitForm() { if (this.imageFileList.length <= 0) { this.msgInfo('请上传图片') return } const loading = this.$loading({ lock: true,//lock的修改符--默认是false text: "保存中",//显示在加载图标下方的加载文案 spinner: "el-icon-loading",//自定义加载图标类名 background: "rgba(0, 0, 0, 0.7)",//遮罩层颜色 target: document.querySelector("#table")//loadin覆盖的dom元素节点 }) this.$refs["form"].validate(valid => { if (valid) { let formData = new FormData() this.imageFileList.forEach(file => { formData.append("imageFileList", file.raw)// 图片列表 }) formData.append("form", JSON.stringify(this.form))//表单 if (this.form.inquiryId != null) { updateInquiry(formData).then(response => { loading.close() this.msgSuccess("修改成功") this.open = false this.getList() }).catch(error => { loading.close() }) } else { addInquiry(formData).then(response => { loading.close() this.msgSuccess("新增成功") this.open = false this.getList() }).catch(error => { loading.close() }) } } else { loading.close() } }) },在表单保存方法中,我们通过FormData将图片文件与表单数据,一起保存。
2024年03月22日
593 阅读
0 评论
0 点赞
2023-08-20
博瑞GE高版本安装第三方App
{message type="warning" content="这款老掉牙的车机,真的不建议安装软件,目前系统自带的软件感觉多少都是有点卡顿。"/}2018款博瑞GE出厂自带的系统是0086版本的,此版本安装软件比较方便。如果是2018款车机,自己或者4S店给升级到了最细系统,建议先降级到0086版本,安装软件后再升级上去。目前只有此版本可以实现免费安装软件,升级到版本后,以下的安装方式,原作者已经开始割韭菜,最低收费59元,一年有效期,也就是说你需要话费59元,在一年之内能够免费安装软件,一年之后仍然需要收费,此安装方式软件少、收费高并且不稳定,最重要的一点是吉利官方已经明确打击这种软件安装方式,出问题可能会影响质保,一定要慎重选择。鉴于大家使用手机共享热点安装的方式比较简单,这里只介绍通过手机热点的安装方法,需要你手机支持双路 WIFI功能,双路WIFI:指的是手机在连接 无线网的同时可以打开个人热点功能,将已连接的无线网络转发共享出去。已知苹果手机不行,大部分安卓机都支持,接下来教程中使用的就是一加手机。零、准备工作手机的方式,需要两部手机,一部手机共享热点,另外一部手机连接到热点后,设置Wifi信息。壹、准备DNS目前我实际验证过的DNS为39.97.123.84,此DNS因为吉利官方打击及其他原因,不保证一直能用,目前我最后一次验证是2023年8月20日,因为收费的原因,我个人是没有使用的。贰、设置手机DNS进入 Wifi 列表页面 >>> 点击右侧感叹号图标 >>> 找到 “IP 设置” >>> 把 “DHCP” 改为 “静态” >>> 下拉找到 域名1 ,把域名1改成39.97.123.84叁、验证DNS配置是否正确以上配置完成后,手机浏览器打开http://www.test.com,如果显示以下内容,说明配置正确截图大家也能看到,是需要授权收费的,比较坑,以前是免费的,后来人多也是开始割韭菜了。肆、安装软件手机设置完成后,打开热点,然后车机链接到手机热点,打开软件商店,此时加载的就不再是吉利官方的那些App了,交钱就能安装了,这里就不截图了,吃香比较难看。
2023年08月20日
2,109 阅读
1 评论
0 点赞
2023-08-12
博瑞GE升级高德版本的一些说明
最近因为高德不能用,又折腾了一把车机,期间逛了一下各类论坛,发现大家对于高德车机升级的事情比较关心,其实这个不难理解,原车自带的高德车机版还是2.X的版本,现在最新的都升级到7.X了。在论坛看到好多下面这种让加微信的水贴,明眼人一眼就能看出来,其实就是【骗钱】的,也不能叫骗钱,不能说这个东西是骗钱吧,每个人很简单都能安装,没必要搞得还是花钱。首先明确一点,原车自带的高德导航能不能升级,答案是能,你可以任意安装,去高德官网去下载公测版安装就可以了。但是我们需要注意的是,原车自带的高德导航,是能够投屏到仪表盘的,如果一单安装了官网的版本,就意味着失去了投屏仪表盘的能力。目前,据我所了解的,除了自带的高德,没有能够投屏到仪表盘的。你即使微信花钱找人安装的,也是不能投到仪表盘的。关于安装软件,0086版本是能够安装软件的,具体教程,可以参考博瑞GE安装第三方App - 李森的博客 (lisen.cc)非0086版本其实也能安装软件,但是相对来说比较麻烦,需要修改DNS,这个稍后我可以专门出个文章,但是无法确保DNS是否能够使用,所以如果希望安装软件,建议先降级到0086版本,然后安装个小白文件管理,然后在升级到较高版本,这样后续就能任意安装软件了。
2023年08月12日
1,901 阅读
0 评论
0 点赞
2023-08-12
博瑞GE车机升级/降级
博瑞GE停产都快两年了,所以这是一篇估计写了也没人看的博文。刚买车那会(18年)就折腾车机升级,后续吉利也不再更新,也就没在折腾过。这次刷机,其实是因为车机的高德导航突然不能定位了,尝试恢复出厂设置,但是问题仍然没有解决,尝试刷机,问题目前来看是解决了。所以特地记录一下博瑞GE的刷机方式。壹、刷机包下载刷机一定要注意,因为我不是最高配的,所以刷机包我只提供了非最高配的刷机包,如果是最高配的,请勿下载。{cloud title="车机刷机包" type="bd" url="https://pan.baidu.com/s/1PsSnfP1Gj4TjswFiKQt0pw?pwd=e9nb" password="e9nb"/}里面有两个刷机包kc_update_01.02.10800.H52.00086.zip这个是18款车辆出厂带的车机,只有这个版本的能安装软件,需要想自己安装软件,记得先在这个版本的基础上安装完,安装之后可以在升级到kc_update_01.02.12000.H52.00016.zipkc_update_01.02.12000.H52.00016.zip这个是目前最新的车机版本。其实升级也是升级了个寂寞,感觉就是换了几个图标还有开机动画。大家按需选择吧。贰、准备工作16G的U盘一个(存放升级包),U盘必须格式化成Fat32格式的,不能是NTFS格式。U盘根目录中只能存在一个升级包,如果是降级再升级,U盘可以单独建文件夹存放暂时不用的升级包。电脑一台(下载升级包)。叁、刷机将升级包放到U盘的根目录,不要重命名更不要解压缩升级包。然后将U盘插入扶手箱的USB1口上。3.1、升级如果是kc_update_01.02.10800.H52.00086.zip升级到kc_update_01.02.12000.H52.00016.zip,可以打开电话,输入#*32281,输入完成后系统会自动打开后台界面,选择【SOC升级】,系统会自动查到U盘中的升级包,点击进行升级即可。3.2、降级如果是kc_update_01.02.12000.H52.00016.zip降级到kc_update_01.02.10800.H52.00086.zip,进入车机主界面,点击下面齿轮装的【设置】,点击【系统】,然后点【关于本机】。按住右侧吉利标志10s左右,此时会弹出输入法,切换输入法输入ns8546,此时会自动进入工程模式。在工程模式页面,点击【SOC升级】系统会自动查找U盘中的降级包,点击升级即可。如果降级之后,还想升级,可以参考上面升级的教程。
2023年08月12日
5,590 阅读
1 评论
13 点赞
1
...
7
8
9
...
52