mirror of
https://github.com/zfile-dev/zfile.git
synced 2025-04-19 05:34:52 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7033fc3fb1 | ||
|
|
8758bd8225 | ||
|
|
94290af50c | ||
|
|
0200483e22 | ||
|
|
8aa497b13b | ||
|
|
1d60395161 | ||
|
|
d4978b0903 | ||
|
|
495da58af0 | ||
|
|
57f012482c | ||
|
|
22a5e5cfeb | ||
|
|
fa332df4cf | ||
|
|
86885c880a | ||
|
|
9ef217c33c | ||
|
|
efbd2b441f | ||
|
|
de7d276825 | ||
|
|
428d04d478 |
44
README.md
44
README.md
@@ -16,13 +16,53 @@
|
||||
* 支持在线浏览文本文件, 视频, 图片, 音乐.
|
||||
* 文件/目录二维码
|
||||
|
||||
## 快速开始
|
||||
|
||||
|
||||
安装 JDK 1.8 :
|
||||
|
||||
```bash
|
||||
yum instal -y java # 适用于 Centos 7.x
|
||||
```
|
||||
|
||||
> 其他系统的 JDK 安装教程, 后续我也都会补上. 大家也可执行搜索安装方式, 应该不是很难.
|
||||
|
||||
下载项目:
|
||||
|
||||
```bash
|
||||
wget https://github.com/zhaojun1998/zfile/releases/download/0.1/zfile-0.1.jar
|
||||
```
|
||||
|
||||
启动项目:
|
||||
|
||||
```bash
|
||||
java -jar zfile-0.1.jar
|
||||
|
||||
## 高级启动
|
||||
java -jar zfile-0.1.jar --server.port=18777
|
||||
```
|
||||
|
||||
> `--server.port` 为指定端口, 默认为 `8080`
|
||||
> 其他参数, 后面我会详细补充至文档, 最晚本周六日.
|
||||
|
||||
|
||||
访问地址:
|
||||
|
||||
用户前台: http://127.0.0.1:8080/#/main
|
||||
|
||||
初始安装: http://127.0.0.1:8080/#/install
|
||||
|
||||
管理后台: http://127.0.0.1:8080/#/admin
|
||||
|
||||
|
||||
|
||||
|
||||
## 运行环境
|
||||
|
||||
* JDK: `1.8`
|
||||
* 缓存: `caffeine/redis`
|
||||
* 数据库: `h2/mysql`
|
||||
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 缓存
|
||||
@@ -51,4 +91,4 @@
|
||||
- 文本预览更换更好用的编辑器
|
||||
- 后台支持上传、编辑、删除等操作
|
||||
- API 支持
|
||||
- 更方便的部署方式
|
||||
- 更方便的部署方式
|
||||
|
||||
5
pom.xml
5
pom.xml
@@ -46,6 +46,11 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>3.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<dependency>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.dto.InstallModelDTO;
|
||||
import im.zhaojun.common.model.dto.ResultBean;
|
||||
@@ -8,7 +9,6 @@ import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.service.SystemConfigService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -58,7 +58,7 @@ public class InstallController {
|
||||
StorageTypeEnum storageTypeEnum = installModelDTO.getStorageStrategy();
|
||||
systemConfigDTO.setStorageStrategy(storageTypeEnum);
|
||||
systemConfigDTO.setUsername(installModelDTO.getUsername());
|
||||
systemConfigDTO.setPassword(new BCryptPasswordEncoder().encode(installModelDTO.getPassword()));
|
||||
systemConfigDTO.setPassword(SecureUtil.md5(installModelDTO.getPassword()));
|
||||
systemConfigDTO.setDomain(installModelDTO.getDomain());
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class SiteConfigDTO implements Serializable {
|
||||
@@ -10,6 +12,7 @@ public class SiteConfigDTO implements Serializable {
|
||||
|
||||
private String footer;
|
||||
|
||||
@JsonProperty("viewConfig")
|
||||
private SystemConfigDTO systemConfigDTO;
|
||||
|
||||
public String getHeader() {
|
||||
|
||||
@@ -14,7 +14,8 @@ public enum StorageTypeEnum {
|
||||
ALIYUN("aliyun", "阿里云 OSS"),
|
||||
FTP("ftp", "FTP"),
|
||||
LOCAL("local", "本地存储"),
|
||||
TENCENT("tencent", "腾讯云 COS");
|
||||
TENCENT("tencent", "腾讯云 COS"),
|
||||
MINIO("minio", "MINIO");
|
||||
|
||||
private static Map<String, StorageTypeEnum> enumMap = new HashMap<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package im.zhaojun.common.security;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
public class MD5PasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return SecureUtil.md5(rawPassword.toString()).equals(encodedPassword);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -116,7 +115,7 @@ public class MySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
public static PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
return new MD5PasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
import im.zhaojun.common.model.SystemConfig;
|
||||
import im.zhaojun.common.model.constant.SystemConfigConstant;
|
||||
@@ -48,6 +49,9 @@ public class SystemConfigService {
|
||||
case SystemConfigConstant.PASSWORD:
|
||||
systemConfigDTO.setPassword(systemConfig.getValue());
|
||||
break;
|
||||
case SystemConfigConstant.DOMAIN:
|
||||
systemConfigDTO.setDomain(systemConfig.getValue());
|
||||
break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +106,7 @@ public class SystemConfigService {
|
||||
usernameConfig.setValue(username);
|
||||
systemConfigRepository.save(usernameConfig);
|
||||
|
||||
password = new BCryptPasswordEncoder().encode(password);
|
||||
password = SecureUtil.md5(password);;
|
||||
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
|
||||
systemConfig.setValue(password);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public class SystemService {
|
||||
* 构建指定路径下标题, 页头, 页尾
|
||||
* @param path 路径
|
||||
*/
|
||||
public SiteConfigDTO getConfig(String path) throws Exception {
|
||||
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
|
||||
|
||||
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
|
||||
FileService fileService = systemConfigService.getCurrentFileService();
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package im.zhaojun.common.util;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class HttpUtil {
|
||||
|
||||
public static String getTextContent(String url) {
|
||||
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class);
|
||||
String result = restTemplate.getForObject(url, String.class);
|
||||
String result = restTemplate.getForObject(URLUtil.decode(url), String.class);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Date;
|
||||
|
||||
@Controller
|
||||
@@ -39,18 +38,13 @@ public class LocalController {
|
||||
}
|
||||
|
||||
private ResponseEntity<FileSystemResource> export(File file) throws IOException {
|
||||
// 获取文件 MIME 类型
|
||||
String fileMimeType = Files.probeContentType(file.toPath());
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
if (fileMimeType == null || "".equals(fileMimeType)) {
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.add("Content-Disposition", "attachment; filename=" + file.getName());
|
||||
} else {
|
||||
mediaType = MediaType.parseMediaType(fileMimeType);
|
||||
}
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
|
||||
|
||||
headers.add("Pragma", "no-cache");
|
||||
headers.add("Expires", "0");
|
||||
headers.add("Last-Modified", new Date().toString());
|
||||
|
||||
@@ -84,7 +84,7 @@ public class LocalServiceImpl implements FileService {
|
||||
@Override
|
||||
public String getDownloadUrl(String path) throws Exception {
|
||||
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
|
||||
return StringUtils.concatPath( usernameConfig.getValue(), "file" + path);
|
||||
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
|
||||
109
src/main/java/im/zhaojun/minio/MinIOServiceImpl.java
Normal file
109
src/main/java/im/zhaojun/minio/MinIOServiceImpl.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package im.zhaojun.minio;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.dto.FileItemDTO;
|
||||
import im.zhaojun.common.model.enums.FileTypeEnum;
|
||||
import im.zhaojun.common.model.enums.StorageTypeEnum;
|
||||
import im.zhaojun.common.service.FileService;
|
||||
import im.zhaojun.common.service.StorageConfigService;
|
||||
import im.zhaojun.common.util.StringUtils;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.Result;
|
||||
import io.minio.messages.Item;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class MinIOServiceImpl implements FileService {
|
||||
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MinIOServiceImpl.class);
|
||||
|
||||
private String bucketName;
|
||||
|
||||
@Value("${zfile.cache.timeout}")
|
||||
private Long timeout;
|
||||
|
||||
private static final String BUCKET_NAME_KEY = "bucket-name";
|
||||
|
||||
private static final String ACCESS_KEY = "accessKey";
|
||||
|
||||
private static final String SECRET_KEY = "secretKey";
|
||||
|
||||
private static final String ENDPOINT_KEY = "endPoint";
|
||||
|
||||
@Resource
|
||||
private StorageConfigService storageConfigService;
|
||||
|
||||
private MinioClient minioClient;
|
||||
|
||||
private boolean isInitialized;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Map<String, StorageConfig> stringStorageConfigMap =
|
||||
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.MINIO);
|
||||
String accessKey = stringStorageConfigMap.get(ACCESS_KEY).getValue();
|
||||
String secretKey = stringStorageConfigMap.get(SECRET_KEY).getValue();
|
||||
String endPoint = stringStorageConfigMap.get(ENDPOINT_KEY).getValue();
|
||||
bucketName = stringStorageConfigMap.get(BUCKET_NAME_KEY).getValue();
|
||||
minioClient = new MinioClient(endPoint, accessKey, secretKey);
|
||||
isInitialized = true;
|
||||
} catch (Exception e) {
|
||||
log.debug(StorageTypeEnum.MINIO.getDescription() + "初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<FileItemDTO> fileList(String path) throws Exception {
|
||||
path = StringUtils.removeFirstSeparator(path);
|
||||
List<FileItemDTO> fileItemList = new ArrayList<>();
|
||||
|
||||
Iterable<Result<Item>> iterable = minioClient.listObjects(bucketName, path, false);
|
||||
|
||||
for (Result<Item> itemResult : iterable) {
|
||||
Item item = itemResult.get();
|
||||
|
||||
FileItemDTO fileItemDTO = new FileItemDTO();
|
||||
if (item.isDir()) {
|
||||
fileItemDTO.setName(StringUtils.removeLastSeparator(item.objectName().replaceFirst(path, "")));
|
||||
fileItemDTO.setType(FileTypeEnum.FOLDER);
|
||||
fileItemDTO.setPath(path);
|
||||
} else {
|
||||
fileItemDTO.setName(item.objectName().replaceFirst(path, ""));
|
||||
fileItemDTO.setSize(item.objectSize());
|
||||
fileItemDTO.setTime(item.lastModified());
|
||||
fileItemDTO.setType(FileTypeEnum.FILE);
|
||||
fileItemDTO.setPath(path);
|
||||
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
|
||||
}
|
||||
fileItemList.add(fileItemDTO);
|
||||
}
|
||||
|
||||
return fileItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String path) throws Exception {
|
||||
return minioClient.presignedGetObject(bucketName, path, timeout.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.MINIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsInitialized() {
|
||||
return isInitialized;
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,13 @@ spring:
|
||||
|
||||
# h2 内存数据库 配置
|
||||
driver-class-name: org.h2.Driver
|
||||
url: jdbc:h2:~/.zfile/db/zfile
|
||||
username: zfile
|
||||
url: jdbc:h2:~/.zfile/db/zfile-demo
|
||||
username: zfile-demo
|
||||
password: 123456
|
||||
|
||||
# MySQL 配置
|
||||
# driver-class-name: com.mysql.jdbc.Driver
|
||||
# url: jdbc:mysql://127.0.0.1:3306/zfile?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
|
||||
# url: jdbc:mysql://127.0.0.1:3306/zfile-demo?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
|
||||
# username: root
|
||||
# password: 123456
|
||||
jackson:
|
||||
@@ -50,6 +50,4 @@ spring:
|
||||
password: 12345
|
||||
zfile:
|
||||
cache:
|
||||
timeout: 300
|
||||
|
||||
|
||||
timeout: 300
|
||||
@@ -36,4 +36,8 @@ INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (25, 'se
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (26, 'secretKey', 'SecretKey', 'tencent', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (27, 'bucket-name', '云存储服务名称', 'tencent', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (28, 'domain', '加速域名', 'tencent', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (29, 'endPoint', '区域', 'tencent', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (29, 'endPoint', '区域', 'tencent', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (30, 'accessKey', 'SecretId', 'minio', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (31, 'secretKey', 'SecretKey', 'minio', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (32, 'endPoint', '服务地址', 'minio', null);
|
||||
INSERT INTO STORAGE_CONFIG (`ID`, `k`, `TITLE`, `TYPE`, `VALUE`) VALUES (33, 'bucket-name', '存储空间名称', 'minio', null);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/main/resources/static/css/chunk-vendors.7d475efd.css
Normal file
1
src/main/resources/static/css/chunk-vendors.7d475efd.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><script src=//at.alicdn.com/t/font_1428963_kk67lnmooll.js></script><title></title><link href=/css/app.a5e07919.css rel=preload as=style><link href=/css/chunk-vendors.2f37471b.css rel=preload as=style><link href=/js/app.07eaa8c9.js rel=preload as=script><link href=/js/chunk-vendors.06c4a2a1.js rel=preload as=script><link href=/css/chunk-vendors.2f37471b.css rel=stylesheet><link href=/css/app.a5e07919.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.06c4a2a1.js></script><script src=/js/app.07eaa8c9.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><script src=//at.alicdn.com/t/font_1428963_kk67lnmooll.js></script><title></title><link href=/css/app.16ea04c8.css rel=preload as=style><link href=/css/chunk-vendors.7d475efd.css rel=preload as=style><link href=/js/app.fa7d2b78.js rel=preload as=script><link href=/js/chunk-vendors.e68c4b36.js rel=preload as=script><link href=/css/chunk-vendors.7d475efd.css rel=stylesheet><link href=/css/app.16ea04c8.css rel=stylesheet></head><body><noscript><strong>We're sorry but zfile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.e68c4b36.js></script><script src=/js/app.fa7d2b78.js></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
src/main/resources/static/js/app.fa7d2b78.js
Normal file
2
src/main/resources/static/js/app.fa7d2b78.js
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/js/app.fa7d2b78.js.map
Normal file
1
src/main/resources/static/js/app.fa7d2b78.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user