兰空图床(lsky-pro)V2.0的自动上传和删除全部

1,863 阅读4分钟

写在前面的话

  • 本util使用java+hutool编写,请悉知。
  • 仅限兰空图床(lsky-pro)V2.0的友友们。
  • 下面代码默认md文件,不过也可以自行修改。

关于图床

  • 什么是图床?
  • 他有什么作用?

什么是图床

  • 图床一般是指储存图片的服务器。也就是说,只要是你关于图片的任何资源,你都可以上传,接着通过返回的链接粘贴到任意能够展示该图片的地方,比如typroa
  • 如果已经明白图床是什么请自行滑动到下方代码处。

举例

这么说可能还不太理解图床。

举个浅显的例子,我的某个文档需要供多方查阅,如果是按照以往的套路,我需要每一份都包含该文档的图片资源,这无形之中加重了存储负担。

也许有人会问,这和阿里云的OSS或是之类的文件资源管理系统有什么区别?

要笔者说就是更具体的分层,比如阿里云OSS可以存更多类型的资源,而图床本质上离不开图片,也就是说,他默认只是存储图片资源的。

唠叨这么久,往下看主题吧。

关于笔者为什么选用兰空图床(lsky-pro)

笔者在N久之前发布过一篇SpringSecurity的文章,里面就带过一笔有群晖NAS这事。

作为一个后端程序员,那肯定得多折腾呀,正好又有时间,对吧。

那为什么选择兰空图床V2.0??????

那当然是它长得好看又易上手哇!(我是真觉得好看!勿cue其他图床,靴靴!)

image.png

因为笔者在V2.0版本上走过坑的缘故,这里推荐一篇关于V2.0部署的文档,在兰空图床GitHub上的(想和笔者配置一样的一定要看!!!因为我认知的版本里lsky-pro作者忘记把静态资源改成https了!当然如果你是http请无视)。
github.com/lsky-org/ls…

本篇文章主题

  • 自动上传是什么?
  • 删除全部是什么?

自动上传

说来惭愧,笔者也是在折腾NAS的时候才知道图床。
平时有很多记录的文档在typora上,图片资源也都各自分散,但是笔者在typora里上传图片时点的鼠标都要擦火了,也就萌生了自己写一篇自动化上传并替换文档的打算。

删除全部

如果看过lsky-pro开放的API就知道,他只有点击按钮单个选择才能删除!想想当初234张图片,好不容易选择到233张,最后手一抖,全取消了!!!
倒吸一口凉气。

image.png 多么吐血!!!(也可能是笔者技术浅薄的原因没找到一键全选)

总而言之,笔者也就通过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("![")) {
                    //md文件中引入图片资源:![xxx](图片资源路径)

                    //获取图片资源路径,分为绝对路径和相对路径
                    String mdFileUrl = string.substring(string.indexOf("(") + 1,string.length() - 1);
                    //如果为绝对路径,例如:C:\xxxx\xxx\xxxx.png
                    if (string.contains(":\")){
                        //![获取绝对路径最后一个\后面的名字+后缀](图床上的图片资源链接)
                        string = "![" + mdFileUrl.substring(mdFileUrl.lastIndexOf("\") + 1) +"](" + getTuChuangUrl(mdFileUrl) +")";
                    }else{
                        //如果为相对路径,例如xxxxx.assets/xxxxx.png
                        //获取当前file路径前缀路径(去除名称+后缀)+相对路径
                        String fileUrl = file.getPath().substring(0,file.getPath().lastIndexOf("\") + 1) + mdFileUrl;
                        //最终结果:![获取当前file路径最后一个/后面的名字+后缀](图床上的图片资源链接)
                        string = "![" + fileUrl.substring(fileUrl.lastIndexOf("/") + 1) +"](" + getTuChuangUrl(fileUrl) +")";
                    }
                }
                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 "替换失败,请检查异常!";
    }

}

效果图

image.png

写在后面的话

理论上来说可以开箱即用,当然也可以自行修改。
如果有任何问题,请在评论区指点,友好交流,共同进步。