一、准备工作
服务器一台、安装nginx.
下载Typora。Typora官网
Typora插件
二、准备上传接口(java实现)
application.yml
# 应用名称
spring:
application:
name: typora-upload
servlet:
multipart:
max-file-size: 1MB
max-request-size: 1MB
enabled: true
server:
port: 8800
upload:
image:
valid:
suffixes: .png #图片格式,多种类型可以,分割。这里只用做截图使用png
fingerling:
upload:
image:
context:
path: /img #图片目录 对应nginx下img文件夹
base:
path: /www/server/nginx/html/img #图片保存路径
online:
path: https://xxx #服务器地址
key: xxxxxxxxxxx #key和secret自定义防止接口破坏
secret: xxxxxxxxxxxxxx
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.directory.studio/org.apache.commons.io -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
@RestController
public class UploadController {
public static final Logger logger = LoggerFactory.getLogger(UploadController.class);
final
UploadUtil uploadUtil;
@Value("${fingerling.upload.image.key}")
private String key;
@Value("${fingerling.upload.image.secret}")
private String secret;
public UploadController(UploadUtil uploadUtil) {
this.uploadUtil = uploadUtil;
}
@PostMapping("/upload")
public ResultBean uploadFile(@RequestParam("fileData")String fileData, @RequestHeader String key, @RequestHeader String secret){
if(key == null && secret == null){
return new ResultBean(01,"非法请求",null);
}else if(!key.equals(key) && !secret.equals(secret)){
return new ResultBean(02,"令牌鉴权失败",null);
}
logger.info("request:{}",fileData);
String img = null;
MultipartFile file = uploadUtil.base64ToMultipart(fileData);
try {
img = uploadUtil.upload(file);
} catch (Exception e) {
e.printStackTrace();
return new ResultBean(03,"上传失败",null);
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("url",img);
return new ResultBean(0,"成功",jsonObject);
}
}
@Component
public class UploadUtil {
@Value("${fingerling.upload.image.base.path}")
private String uploadPath;
@Value("${fingerling.upload.image.context.path}")
private String contextPath;
@Value("${fingerling.upload.image.online.path}")
private String onlinePath;
@Value("${upload.image.valid.suffixes}")
private String validSuffix;
public static final Logger logger = LoggerFactory.getLogger(UploadUtil.class);
public String upload(MultipartFile file) throws IOException {
//获取文件名称;
String name = file.getOriginalFilename();
//获取当前时间作为最终存储文件的上级目录;
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
String today = format.format(new Date());
//保存的文件名:UUID+文件后缀;
String fileName = UUID.randomUUID().toString()+ getFileSuffix(name);
//文件类型校验;
String type = getFileSuffix(name);
logger.info("文件类型:{}",type);
if (!validSuffix.contains(type)) {
logger.error("UploadFile failed for suffix error;error sufiix:" + type);
throw new IOException("M2-000001");
}
//文件最终上传地址;
String uploadFolder = uploadPath + File.separator + today;
//上传文件;
File dir = new File(uploadFolder);
if (!dir.exists()) {
dir.mkdirs();
}
String targetUploadPath = dir + File.separator + fileName;
String targetContextPath = contextPath + File.separator + today + File.separator + fileName;
try {
FileUtils.writeByteArrayToFile(new File(targetUploadPath), file.getBytes());
} catch (IOException e) {
logger.error("UploadFile failed", e);
throw new IOException("M2-000002");
}
//返回文件存储位置;
return onlinePath+targetContextPath;
}
public static String getFileSuffix(String name) {
return name.substring(name.lastIndexOf("."), name.length());
}
public MultipartFile base64ToMultipart(String base64) {
try {
String[] baseStrs = base64.split(",");
BASE64Decoder decoder = new BASE64Decoder();
byte[] b = new byte[0];
b = decoder.decodeBuffer(baseStrs[1]);
for(int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
return new Base64DecodeMultipartFile(b, baseStrs[0]);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
public class Base64DecodeMultipartFile implements MultipartFile {
private final byte[] imgContent;
private final String header;
public Base64DecodeMultipartFile(byte[] imgContent, String header) {
this.imgContent = imgContent;
this.header = header.split(";")[0];
}
@Override
public String getName() {
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
}
@Override
public String getOriginalFilename() {
return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
}
@Override
public String getContentType() {
return header.split(":")[1];
}
@Override
public boolean isEmpty() {
return imgContent == null || imgContent.length == 0;
}
@Override
public long getSize() {
return imgContent.length;
}
@Override
public byte[] getBytes() throws IOException {
return imgContent;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(imgContent);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
}
三、配置Typora插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4a0lscc-1612377823986)(https://blog.lihaijian.top/upload/2021/02/image-71a93917cb2442e0aa6920193cb264bf.png)]
将plugins文件夹和window.html移动到
配置plugins文件夹下的upload.js
文件最后设置上传路径和请求头
四、重启Typora
问题:该版本会出现重复上传和导致偏好设置打不开问题。
解决方案