页面元素替换

首先要做的当然是换成自己风格的站名和内容啦。

1、网站配置

跟踪前端代码后发现配置是来自后端接口,想着既然入库了,那应该有对应的管理页面吧,果然找到了,就是…演示账号不允许操作!那么接下来要干的事就很明显了,把这个用户搞定!

2、账号配置

切换到idea发现工作台存在一行报错,根据报错跳转到对应的代码,发现这鉴权方式没见过啊!赶紧面向百度编程。

Sa-token文档地址:https://sa-token.dev33.cn/
大概了解了下这个框架,简直是懒人福音啊x。然后发现页面上就有用户管理 + 修改密码,那么事情就变得简单了。

3、文件上传

因为预想中配置的文件服务器是minio,作者只附了本地和七牛两种方式,那么改造开始。

增加minio标签选项

全局搜索图片上传方式,找到对应绑定的字段,加上minio。PS:阿里oss原本也是没有的,但是跟踪后端代码发现字典值2对应的是阿里oss,就先加上了。

后端代码

跟踪/file/upload接口可以发现,后端是根据fileUploadWay 配置字段决定调用哪个上传策略。

private void getFileUploadWay() {strategy = FileUploadModelEnum.getStrategy(systemConfigService.getCustomizeOne().getFileUploadWay());
}

跟踪FileUploadModelEnum发现是个枚举类,那么先加上minio的枚举。

先在pom.xml引入minio

<!-- Minio -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version>
</dependency>

原作者的配置方式是在数据库加入字段,这种方式不太习惯,所以这边minio的配置都加入到配置文件中,后续使用**@Value**注入。

#============================Minio配置信息===================================
minio:url: http://ip:9000accessKey: minio账号secretKey: minio密码bucketName: 桶名称preurl: http://预览地址

随后仿造aliUploadStrategyImpl创建minio对应的service。

package com.shiyi.strategy.imp;import com.shiyi.strategy.FileUploadStrategy;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.velocity.shaded.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.stream.Collectors;@Service("minioUploadStrategyImpl")
public class MinioUploadStrategyImpl implements FileUploadStrategy {private final Logger logger = LoggerFactory.getLogger(MinioUploadStrategyImpl.class);/*** 服务地址*/@Value("${minio.url}")private String url;/*** 预览路径前缀*/@Value("${minio.preurl}")private String preurl;/*** 用户名*/@Value("${minio.accessKey}")private String accessKey;/*** 密码*/@Value("${minio.secretKey}")private String secretKey;/*** 存储桶名称*/@Value("${minio.bucketName}")private String bucketName;private static MinioClient client = null;@PostConstructprivate void init(){client = MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();}@Overridepublic String fileUpload(MultipartFile file,String suffix) {String fileName = null;try {String extension = FilenameUtils.getExtension(file.getOriginalFilename());fileName = DateFormatUtils.format(new Date(), "yyyy/MM/dd") + "/" + UUID.randomUUID() + "." + extension;PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();client.putObject(args);} catch (Exception e) {e.printStackTrace();}return preurl + "/" + bucketName + "/" + fileName;}/*** 删除文件 -- minio** @param key   文件url* @return      ResponseResult*/@Overridepublic Boolean deleteFile(String ...key) {if (key.length > 0) {//批量删除Iterable<DeleteObject> deleteObjects = Arrays.stream(key).map(s -> new DeleteObject(s)).collect(Collectors.toList());Iterable<Result<DeleteError>> results = client.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build());for (Result<DeleteError> result : results) {try {result.get();} catch (Exception e) {logger.error(e.getMessage());e.printStackTrace();}}}return true;}}

先在入口添加一下注解,再使用swagger调用测试,PS:记得先登录
![image.png](https://img-blog.csdnimg.cn/img_convert/25de1564a7752823ed66d8d1f751c3fb.png#averageHue=#2c2c2b&clientId=u5ccd04dd-8879-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=199&id=u6c178023&margin=[object Object]&name=image.png&originHeight=199&originWidth=2033&originalType=binary&ratio=1&rotation=0&showTitle=false&size=57089&status=done&style=none&taskId=u7ed14748-0b8e-4b09-b099-6807f1e7572&title=&width=2033)

文件中间表

为啥要用中间表呢,主要是想保护minio的端口。上传和下载都通过代码进行,就不能通过文件层级猜到别的文件路径。以及防止minio突然暴露什么漏洞。【当然如果是项目上用这个才不管呢!】

  1. 建表语句
CREATE TABLE `tb_files` (`id` bigint(20) NOT NULL COMMENT '主键id',`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`preview_file` varchar(128) DEFAULT NULL COMMENT '文件minio地址',`file_name` varchar(512) DEFAULT NULL COMMENT '原文件名称',`content_type` varchar(50) DEFAULT NULL COMMENT '文件类型',`is_static` tinyint(1) DEFAULT '0' COMMENT '是否静态资源',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='文件表';
  1. 随便抄一个代码生成器
/*** 代码生成器*/
public class CodeGenerator {public static void main(String[] args) {// 1、创建代码生成器AutoGenerator mpg = new AutoGenerator();// 2、全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");gc.setAuthor("dingx");gc.setOpen(false); //生成后是否打开资源管理器gc.setFileOverride(false); //重新生成时文件是否覆盖/** mp生成service层代码,默认接口名称第一个字母有 I* UcenterService* */gc.setServiceName("%sService"); //去掉Service接口的首字母Igc.setIdType(IdType.ASSIGN_ID); //主键策略gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型gc.setSwagger2(true);//开启Swagger2模式mpg.setGlobalConfig(gc);// 3、数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://ip:port/schema?serverTimezone=GMT%2B8");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("pwd");dsc.setDbType(DbType.MYSQL);mpg.setDataSource(dsc);// 4、包配置PackageConfig pc = new PackageConfig();
//        pc.setModuleName(scanner("模块名")); //模块名pc.setParent("com.shiyi");pc.setController("controller");pc.setEntity("entity");pc.setService("service");pc.setMapper("mapper");mpg.setPackageInfo(pc);// 5、策略配置StrategyConfig strategy = new StrategyConfig();strategy.setInclude(scanner("表名"));strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain =true) setter链式操作strategy.setRestControllerStyle(true); //restful api风格控制器strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符mpg.setStrategy(strategy);// 6、执行mpg.execute();}private static String scanner(String tip) {Scanner scanner = new Scanner(System.in);System.out.println(("请输入" + tip + ":"));if (scanner.hasNext()) {String ipt = scanner.next();if (StringUtils.isNotBlank(ipt)) {return ipt;}}throw new MybatisPlusException("请输入正确的" + tip + "!");}
}
  1. 改造上传代码
private final TbFilesService tbFilesService;@Override
public String fileUpload(MultipartFile file,String suffix) {String fileName;TbFiles tbFile = null;try {String extension = getExtension(file);fileName = DateFormatUtils.format(new Date(), "yyyy/MM/dd") + "/" + UUID.randomUUID() + "." + extension;//保存上传文件记录tbFile = new TbFiles(file.getOriginalFilename(), fileName, file.getContentType());if (!tbFilesService.save(tbFile)){throw new RuntimeException("插入文件失败");}PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();client.putObject(args);} catch (Exception e) {e.printStackTrace();}return preurl + "/" + tbFile.getId();
}
  1. 增加预览接口
public class TbFilesController {private final TbFilesService filesService;private final MinioUploadStrategyImpl minioUploadStrategy;/*** 预览* @param id* @return*/@SaIgnore@GetMapping("/preview/{id}")@ResponseBodypublic ResponseEntity<StreamingResponseBody> preview(@PathVariable Long id){TbFiles file = filesService.getDetail(id);//设置头文件Content-typeHttpHeaders headers = new HttpHeaders();// 发送给客户端的数据// 设置编码if (StringUtils.isNotBlank(file.getContentType())) {headers.setContentType(MediaType.valueOf(file.getContentType()));}//构造返回体return ResponseEntity.ok().headers(headers).body(outputStream -> {try (InputStream inputStream = minioUploadStrategy.downloadFile(file.getPreviewFile())){IOUtils.copy(inputStream, outputStream);} catch (Exception e){e.printStackTrace();}});}
}

这里遇到的坑:
1)使用了ResponseEntity作为返回对象,使用HttpServletResponse的话,Content-type变更了也会被Spring框架自动更改为application/json。查找资料的时候看到很多使用**@GetMappingproduces属性,但是这样就固定了Content-type的内容。
2)不能使用下载的方式获取预览流,
标签中放入地址后虽然接口调用成功了,但是图是裂开的。
3)接口校验忽略接口 @SaIgnore 这个注解是sa-token 1.29版本没有的。这里升级到了
1.32版本。当然也可以改WebMvcConfig文件中的sa-token**拦截器。

  1. 功能测试

上传后查看数据库,已经入库。

调用preview方法

  1. 一个警告
!!!
An Executor is required to handle java.util.concurrent.Callable return values.
Please, configure a TaskExecutor in the MVC config under "async support".
The SimpleAsyncTaskExecutor currently in use is not suitable under load.
-------------------------------
Request URI: '/dingx/data/files/preview/1594875366335397890'
!!!

老实说,写了那么久代码第一次遇到warning提示。。
大意就是:默认的SimpleAsyncTaskExecutor已不适用,请自定义一个TaskExecutor。那就加呗,WebMvcConfig加入下列代码。

@Bean
public ThreadPoolTaskExecutor mvcTaskExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(100);taskExecutor.setMaxPoolSize(100);return taskExecutor;
}@Overridepublic void configureAsyncSupport(AsyncSupportConfigurer configurer) {configurer.setTaskExecutor(mvcTaskExecutor());
}

Nginx强化配置

nginx缓存

proxy_cache_path /root/cache levels=1:2 keys_zone=xd_cache:10m max_size=1g inactive=60m use_temp_path=off;server {location /{... proxy_cache xd_cache;proxy_cache_valid 200 304 10m;proxy_cache_valid 404 1m; proxy_cache_key $host$uri$is_args$args;add_header Nginx-Cache "$upstream_cache_status";}
}
配置讲解:
  1. /root/cache:本地路径,用来设置Nginx缓存资源的存放地址
  2. levels=1:2 :默认所有缓存文件都放在上面指定的根路径中,可能影响缓存的性能,推荐指定为 2
  3. 级目录来存储缓存文件;1和2表示用1位和2位16进制来命名目录名称。第一级目录用1位16进制命名,如a;第二级目录用2位16进制命名,如3a。所以此例中一级目录有16个,二级目录有16*16=256个,总目录数为16256=4096个。
  4. 当levels=1:1:1时,表示是三级目录,且每级目录数均为16个
  5. key_zone:在共享内存中定义一块存储区域来存放缓存的 key 和 metadata
  6. max_size :最大 缓存空间, 如果不指定会使用掉所有磁盘空间。当达到 disk 上限后,会删除最少使用的 cache
  7. inactive:某个缓存在inactive指定的时间内如果不访问,将会从缓存中删除
  8. proxy_cache_valid:配置nginx cache中的缓存文件的缓存时间,proxy_cache_valid 200 304 2m 对于状态为200和304的缓存文件的缓存时间是2分钟
  9. use_temp_path:建议为 off,则 nginx 会将缓存文件直接写入指定的 cache 文件中
  10. proxy_cache:启用proxy cache,并指定key_zone,如果proxy_cache off表示关闭掉缓存
  11. add_header Nging-Cache “$upstream_cache_status”:用于前端判断是否是缓存,miss、hit、expired(缓存过期)、updating(更新,使用旧的应答),还原nginx配置,只保留upstream模块
注意:
  1. nginx缓存过期影响的优先级进行排序为:inactvie > 源服务器端Expires/max-age > proxy_cache_valid
  2. 如果出现 Permission denied 修改nginx.conf,将第一行修改为 user root
  3. 默认情况下GET请求及HEAD请求会被缓存,而POST请求不会被缓存,并非全部都要缓存,可以过滤部分路径不用缓存

vue项目部署至nginx,路由404

查看官网推荐配置,cv一份。

文章SEO

先在百度搜索资源站配置好自己的网站:http://data.zz.baidu.com/linksubmit/index
找到普通收录,在配置文件中增加配置项

baidu:url: http://data.zz.baidu.com/urls?site=blog.dinganwang.top&token=sourceurl: https://blog.dinganwang.top/articles/

修改articleSeo方法。作者这边是用for循环实现的批量推送,emmmm老实说有点怪,所以稍微改了下。

@Value("${baidu.url}")
private String baiduUrl;@Value("${baidu.sourceurl}")
private String sourceUrl;private final static String SUCCESS = "success";
private final static String REMAIN = "remain";/***  文章百度推送* @return*/
@Override
public ResponseResult articleSeo(List<Long> ids) {String param = "";for (Long id : ids) {param += sourceUrl + id + "\n";}HttpEntity<String> entity = new HttpEntity<>(param.trim(), createBdHeader());String res = restTemplate.postForObject(baiduUrl, entity, String.class);JSONObject JO = JSONObject.parseObject(res);if (JO.getInteger(SUCCESS) > 0){return ResponseResult.success("成功推送【" + JO.getInteger(SUCCESS) + "】条,剩余量【" + JO.getInteger(REMAIN) + "】条");}else {return ResponseResult.error("推送失败!");}
}/*** 构造百度seo头文件* @return*/
private static HttpHeaders createBdHeader(){HttpHeaders headers = new HttpHeaders();headers.add("Host", "data.zz.baidu.com");headers.add("User-Agent", "curl/7.12.1");headers.add("Content-Length", "83");headers.add("Content-Type", "text/plain");return headers;
}

第二天能够查看头一天的推送情况。

拾壹博客拆解,页面元素替换(二)相关推荐

  1. 拾壹博客拆解,docker环境部署加自动化发布(一)

    前言 一直想搭建一个属于自己的博客,之前使用过Wordpress,但是觉得太笨重了,很多功能都不是自己需要的,然后有一天摸鱼摸到了拾壹博客.感觉这个项目虽然功能少,但是基本上架子都有了,自己想要的功能 ...

  2. 拾壹博客的启动及一些注意事项

    一.后端(blog)启动报错项以及注意事项: 1.后台如果使用JDK17启动会报错: Caused by: java.lang.NullPointerException: Cannot invoke ...

  3. HTML点击元素页面回到顶部,博客园页面添加返回顶部 TOP 按钮

    博客园页面添加返回顶部 TOP 按钮 TOP 按钮 进入网页管理 -> 设置 在 "页面定制 CSS 代码" 中添加如下 CSS 样式, 当然你可以改为自己喜欢的样式 此处可 ...

  4. 前端 - 博客系统(页面设计) - JavaEE初阶 - 细节狂魔

    文章目录 前言 博客系统 博客列表页 紧急修改一下背景图片,虽然上面那张图很美,但是小了.特别的模糊,我就在替换一下背景图 html 代码部分 通用 css 代码部分 博客列表页专属 css文件 博客 ...

  5. 前端 - 博客系统(页面设计)

    博客系统(页面设计) 实现一个简单的博客系统. 当前先完成页面设计的部分. 通过前面学习的前端知识来构建出网页. 主要分成四个页面: 博客列表页 博客正文页 博客登陆页 博客编辑页 预期效果 博客列表 ...

  6. 其他:博客园页面美化

    看了其他大佬的博客园页面都那么好看,我也决定美化一下自己的博客园,顺便在这里分享一下我的博客园页面美化的方法.这里我选用的模版是SimpleMemory. 效果图 页面定制CSS代码 1)页面透明 # ...

  7. 制作支付宝博客捐款页面

    我想很多朋友在博客圈子闲逛的时候也见过类似内容:文章底部有个向作者捐款的按钮,点击之后就到了对方预先设置的支付宝收款页面.在一些大型博客,往往美其名曰打赏作者!其实这个功能想要实现的话挺简单的,只需要 ...

  8. 博客园页面源代码结构分析

    博客园页面源代码结构分析 一.总结 板块注释结束可以加上end 二.博客园页面源代码结构分析 1.页面样例: 2.页面源代码分析: 1 <!DOCTYPE html> 2 <html ...

  9. Spring boot个人博客【博客详细页面】---学习03

    文章目录 一.思维导图 二.个人博客 1.创建博客详情页面 2.运行效果 3.添加二维码 4.运行查看效果图 5.博客信息 6.运行查看效果 7.留言区域 8.运行查看效果 一.思维导图 二.个人博客 ...

最新文章

  1. golang中文文档_Golang 标准库 限流器 time/rate 设计与实现
  2. php两次访问时间,php – 检查当前时间是否介于两次之间,可能会有几天的时间
  3. 人工智能时代背景下,NLP方向或将悄悄崛起
  4. OpenCASCADE:Open CASCADE Application Framework (OCAF)的简介
  5. 技术人如何自我提升?阿里技术带来丰富学习资源
  6. 智能指针shared_ptr的几个例子
  7. 化学人学python有前途吗-Java之父点赞的开源Java深度学习框架,你不学下吗?
  8. 现场操作前,软件界面的各个功能面板应该通过拖动进行合理布局.请看参考图
  9. 怎样让硬盘分区显示整数大小
  10. MongoDB数据同步工具之 MongoShake
  11. 使用批处理实现分组多用户的创建和权限分配
  12. 第三方支付平台业务分析
  13. 安川A1000变频器通过DriveWizard Plus设置加减速时间
  14. PowerBuilder常用函数
  15. ios视频直播没有音频问题
  16. STM32F1单片机零基础学习(1)
  17. pr cpu100%_打工度假签证拿PR三步搞定!高薪,稳定工作,分分钟成为人生赢家!...
  18. acwing 3548.双端队列
  19. usnews美国大学计算机科学排名,最新!2019年USNews美国大学计算机专业排名
  20. React: setState解析

热门文章

  1. 百度地图 显示,定位,轮廓图
  2. FsatLED mixy第三方自制库 轻松玩转WS2812灯带
  3. httprunner 3.x学习20 - jmespath取值特殊字符问题解决
  4. TextView实现自动上下滚动的效果(TextSwitcher)
  5. Kali虚拟机桥接模式设置和虚拟网卡问题
  6. 场强和电阻_电路中电阻,电压,电流的关系是什么?
  7. 计算机大赛搞笑id,你见过最搞笑的游戏ID是什么?能想到这些的都是人才
  8. GDOI2016模拟8.16帮派
  9. 使用 DiskGenius 执行硬盘分区时提示”格式化时出现错误“怎么解决
  10. sharepoint服务器文件迁移,SharePoint 服务器端对象迁移文件夹