写在前面的话
- 本util使用java+hutool编写,请悉知。
- 仅限兰空图床(lsky-pro)V2.0的友友们。
- 下面代码默认md文件,不过也可以自行修改。
关于图床
- 什么是图床?
- 他有什么作用?
什么是图床
- 图床一般是指储存图片的服务器。也就是说,只要是你关于图片的任何资源,你都可以上传,接着通过返回的链接粘贴到任意能够展示该图片的地方,比如typroa。
- 如果已经明白图床是什么请自行滑动到下方代码处。
举例
这么说可能还不太理解图床。
举个浅显的例子,我的某个文档需要供多方查阅,如果是按照以往的套路,我需要每一份都包含该文档的图片资源,这无形之中加重了存储负担。
也许有人会问,这和阿里云的OSS或是之类的文件资源管理系统有什么区别?
要笔者说就是更具体的分层,比如阿里云OSS可以存更多类型的资源,而图床本质上离不开图片,也就是说,他默认只是存储图片资源的。
唠叨这么久,往下看主题吧。
关于笔者为什么选用兰空图床(lsky-pro)
笔者在N久之前发布过一篇SpringSecurity的文章,里面就带过一笔有群晖NAS这事。
作为一个后端程序员,那肯定得多折腾呀,正好又有时间,对吧。
那为什么选择兰空图床V2.0??????
那当然是它长得好看又易上手哇!(我是真觉得好看!勿cue其他图床,靴靴!)
因为笔者在V2.0版本上走过坑的缘故,这里推荐一篇关于V2.0部署的文档,在兰空图床GitHub上的(想和笔者配置一样的一定要看!!!因为我认知的版本里lsky-pro作者忘记把静态资源改成https了!当然如果你是http请无视)。
github.com/lsky-org/ls…
本篇文章主题
- 自动上传是什么?
- 删除全部是什么?
自动上传
说来惭愧,笔者也是在折腾NAS的时候才知道图床。
平时有很多记录的文档在typora上,图片资源也都各自分散,但是笔者在typora里上传图片时点的鼠标都要擦火了,也就萌生了自己写一篇自动化上传并替换文档的打算。
删除全部
如果看过lsky-pro开放的API就知道,他只有点击按钮单个选择才能删除!想想当初234张图片,好不容易选择到233张,最后手一抖,全取消了!!!
倒吸一口凉气。
多么吐血!!!(也可能是笔者技术浅薄的原因没找到一键全选)
总而言之,笔者也就通过lsky-pro开放的删除图片API来改造了。
代码
该有的注释也都有了,友友们也可自行修改替换。
package com.yueranzs;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
/**
* 读取某目录的md文件并解析其中的图片上传至图床
* 覆盖原md文件
* @author yueranzs
* @date 2022/3/28 22:34
*/
public class ReplaceUtil {
/*网站基本路径,lsky-pro仪表盘->接口->接口URL*/
private static String baseUrl = "https://xxxxxxxxx/api/v1";
/*token*/
private static String token = null;
/**
* 获取token,post
* @return
*/
private static String getToken(String username,String password){
HashMap<String, Object> map = new HashMap<>(2);
map.put("email",username);
map.put("password",password);
// 携带参数发送请求并封装
String post = HttpUtil.post(baseUrl + "/tokens", map, 5000);
JSONObject result = JSONUtil.parseObj(JSONUtil.parseObj(post).get("data").toString());
//拼接token
token = "Bearer " + result.get("token");
return token;
}
/**
* 获取上传图片后的地址
* @param fileUrl 请求的文件本地路径
* @return
*/
private static String getTuChuangUrl(String fileUrl){
// token判空
if (ObjectUtil.isEmpty(token)){
return "";
}
// 携带参数发送请求并封装
String body = "";
while (true){
body = HttpRequest.post(baseUrl + "/upload")
.header("Authorization", token)
.form("file", FileUtil.file(fileUrl))
.timeout(20000)
.execute().body();
JSONObject object = JSONUtil.parseObj(body);
// 为true才能跳出循环,否则一直提交
if ("true".equals(object.get("status").toString())){
break;
}
}
JSONObject data = JSONUtil.parseObj(JSONUtil.parseObj(body).get("data"));
JSONObject links = JSONUtil.parseObj(data.get("links"));
return links.get("url").toString();
}
/**
* 读取目录的文件并替换
* @return
*/
private static String replaceUrl(List<File> files){
files.forEach(file -> {
//依次读取
FileReader fileReader = FileReader.create(file);
List<String> strings = fileReader.readLines();
StringBuilder writeBuffer = new StringBuilder();
strings.forEach(string ->{
if (string.contains("
//获取图片资源路径,分为绝对路径和相对路径
String mdFileUrl = string.substring(string.indexOf("(") + 1,string.length() - 1);
//如果为绝对路径,例如:C:\xxxx\xxx\xxxx.png
if (string.contains(":\")){
//
string = " +")";
}else{
//如果为相对路径,例如xxxxx.assets/xxxxx.png
//获取当前file路径前缀路径(去除名称+后缀)+相对路径
String fileUrl = file.getPath().substring(0,file.getPath().lastIndexOf("\") + 1) + mdFileUrl;
//最终结果:
string = " +")";
}
}
writeBuffer.append(string);
//给每一行追加换行
writeBuffer.append(System.getProperty("line.separator"));
});
//覆盖先前的file
FileWriter fileWriter = FileWriter.create(FileUtil.newFile(file.getPath()));
fileWriter.write(writeBuffer.toString());
System.out.println(file.getPath() + "已替换完毕!");
});
return "ok";
}
/**
* 依次遍历获取图片记录的密钥(key)
* @param currentPage 当前页
* @param keys 首次为空
* @return
*/
private static List<String> getImageKeysByPage(Integer currentPage,List<String> keys){
String str = HttpRequest.get(baseUrl + "/images?page=" + currentPage)
.header("Authorization", token)
.timeout(20000)
.execute().body();
JSONObject dataParent = JSONUtil.parseObj(JSONUtil.parseObj(str).get("data"));
List<JSONObject> dataChilds = JSONUtil.toList((JSONArray) dataParent.get("data"),JSONObject.class);
Integer lastPage = Integer.parseInt(dataParent.get("last_page").toString());
keys.addAll(dataChilds.stream().map(jsonObject -> jsonObject.get("key").toString()).collect(Collectors.toList()));
if (ObjectUtil.isNotEmpty(dataChilds) && currentPage + 1<= lastPage) {
getImageKeysByPage(currentPage + 1,keys);
}
return keys;
}
/**
* 一次性清空所有图片记录
* 注意,经查验,兰空图床只会删除图片记录(还需自行清理图片数据)
* @param username 邮箱
* @param password 密码
* @return
*/
public static String deleteImagesAll(String username,String password){
getToken(username,password);
List<String> keys = new ArrayList<>();
getImageKeysByPage(1,keys);
for (int i = 0; i < keys.size(); i++) {
// 为什么用死循环?
// 调用兰空图床时如果频繁调用,返回的status为false,message为"Too Many Attempts."
// 意思是太多次尝试,所以这里添加死循环,当然也可以自行改成thread.sleep()
while (true){
String resp = HttpRequest.delete(baseUrl + "/images/" + keys.get(i))
.header("Authorization", token)
.execute()
.charset("gbk")
.body();
JSONObject object = JSONUtil.parseObj(resp);
if ("true".equals(object.get("status").toString())){
break;
}
}
System.out.println("已删除第" + (i + 1) + "张图片。");
}
return "已删除完毕!";
}
/**
* 上传并替换md文件
* @param username
* @param password
* @param directoryUrl
* @return
*/
public static String replace(String username,String password,String directoryUrl){
getToken(username,password);
List<File> files = FileUtil.loopFiles(directoryUrl);
//过滤出md文件
List<File> newFiles = files.stream().filter(file -> "md".equals(FileUtil.getSuffix(file))).collect(Collectors.toList());
if ("ok".equals(replaceUrl(newFiles))) {
return "全部替换完毕,请打开文件目录查看!";
}
return "替换失败,请检查异常!";
}
}
效果图
写在后面的话
理论上来说可以开箱即用,当然也可以自行修改。
如果有任何问题,请在评论区指点,友好交流,共同进步。