From 379ecf6b6cb4ecaf65972b56c37bc86abc8fb91b Mon Sep 17 00:00:00 2001 From: 00fly Date: Tue, 5 Nov 2024 11:08:36 +0800 Subject: [PATCH] =?UTF-8?q?Web=E6=96=87=E4=BB=B6=E5=8F=8A=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E5=85=B1=E4=BA=AB=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dockerfile" | 14 ++ .../README.md" | 4 + .../docker/docker-compose.yml" | 25 +++ .../docker/restart.sh" | 4 + .../docker/stop.sh" | 3 + .../pom.xml" | 147 +++++++++++++++++ .../com/fly/FilesSendBootApplication.java" | 37 +++++ .../com/fly/core/auth/AuthInterceptor.java" | 49 ++++++ .../com/fly/core/config/Knife4jConfig.java" | 62 +++++++ .../com/fly/core/config/WebMvcConfig.java" | 98 +++++++++++ .../exception/GlobalExceptionHandler.java" | 63 ++++++++ .../core/exception/ValidateException.java" | 16 ++ .../qr/BufferedImageLuminanceSource.java" | 108 +++++++++++++ .../java/com/fly/core/qr/QRCodeUtil.java" | 153 ++++++++++++++++++ .../filesend/controller/ApiController.java" | 84 ++++++++++ .../filesend/controller/IndexController.java" | 69 ++++++++ .../controller/file/FileController.java" | 103 ++++++++++++ .../controller/file/RestFileController.java" | 106 ++++++++++++ .../controller/file/TextController.java" | 23 +++ .../com/fly/filesend/entity/JsonResult.java" | 65 ++++++++ .../fly/filesend/service/TextService.java" | 20 +++ .../fly/filesend/service/TextService2.java" | 50 ++++++ .../fly/filesend/service/TokenService.java" | 84 ++++++++++ .../src/main/resources/application-dev.yml" | 16 ++ .../src/main/resources/application-prod.yml" | 16 ++ .../src/main/resources/application-test.yml" | 16 ++ .../src/main/resources/application.yml" | 13 ++ .../src/main/resources/img/dog.jpg" | Bin 0 -> 28309 bytes .../src/main/resources/static/error/404.html" | 31 ++++ .../src/main/resources/templates/index.html" | 138 ++++++++++++++++ 30 files changed, 1617 insertions(+) create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/Dockerfile" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/README.md" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/docker-compose.yml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/restart.sh" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/stop.sh" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/pom.xml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/FilesSendBootApplication.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/auth/AuthInterceptor.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/Knife4jConfig.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/WebMvcConfig.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/GlobalExceptionHandler.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/ValidateException.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/BufferedImageLuminanceSource.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/QRCodeUtil.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/ApiController.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/IndexController.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/FileController.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/RestFileController.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/TextController.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/entity/JsonResult.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService2.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TokenService.java" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-dev.yml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-prod.yml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-test.yml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application.yml" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/img/dog.jpg" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/static/error/404.html" create mode 100644 "00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/templates/index.html" diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/Dockerfile" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/Dockerfile" new file mode 100644 index 0000000..bb9266f --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/Dockerfile" @@ -0,0 +1,14 @@ +#基础镜像 +FROM adoptopenjdk/openjdk8-openj9:alpine-slim + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone + +#拷贝发布包 +COPY target/*.jar /app.jar + +EXPOSE 8080 + +CMD ["--server.port=8080"] + +#启动脚本 +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"] diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/README.md" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/README.md" new file mode 100644 index 0000000..17e68c3 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/README.md" @@ -0,0 +1,4 @@ +# Web文件共享工具 + + +功能演示: http://124.71.129.204:8080/ \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/docker-compose.yml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/docker-compose.yml" new file mode 100644 index 0000000..1ee5ef7 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/docker-compose.yml" @@ -0,0 +1,25 @@ +version: '3.7' +services: + luck-share: + image: registry.cn-shanghai.aliyuncs.com/00fly/springboot-luck-share:1.1.0 + container_name: luck-share + deploy: + resources: + limits: + cpus: '1' + memory: 200M + reservations: + cpus: '0.05' + memory: 200M + ports: + - 8080:8080 + environment: + TOKEN_VALIDE_HOURS: 3 + SYSTEM_AUTO_LOGIN: true + restart: on-failure + logging: + driver: json-file + options: + max-size: 5m + max-file: '1' + diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/restart.sh" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/restart.sh" new file mode 100644 index 0000000..1a8d9fa --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/restart.sh" @@ -0,0 +1,4 @@ +#!/bin/bash +docker-compose down && docker system prune -f && docker-compose --compatibility up -d + + diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/stop.sh" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/stop.sh" new file mode 100644 index 0000000..48fce7b --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/docker/stop.sh" @@ -0,0 +1,3 @@ +#!/bin/bash +docker-compose down + diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/pom.xml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/pom.xml" new file mode 100644 index 0000000..85306ba --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/pom.xml" @@ -0,0 +1,147 @@ + + + 4.0.0 + com.fly + springboot-luck-share + 1.1.0 + jar + + + org.springframework.boot + spring-boot-starter-parent + 2.2.4.RELEASE + + + + + UTF-8 + yyyyMMdd-HH + registry.cn-shanghai.aliyuncs.com + 1.8 + true + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.tomcat.embed + tomcat-embed-websocket + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + com.github.xiaoymin + knife4j-spring-boot-starter + 2.0.8 + + + com.google.zxing + core + 3.4.0 + + + + org.apache.commons + commons-lang3 + + + commons-io + commons-io + 2.15.0 + + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-devtools + true + + + + + ${project.artifactId}-${project.version} + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + io.fabric8 + docker-maven-plugin + 0.40.3 + + + package + + build + push + remove + + + + + + + + ${docker.hub} + + + + + ${docker.hub}/00fly/${project.artifactId}:${project.version} + + + ${project.basedir} + + + + + + + + + src/main/java + + **/*.java + + + + src/main/resources + + **/** + + false + + + + diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/FilesSendBootApplication.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/FilesSendBootApplication.java" new file mode 100644 index 0000000..89a369e --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/FilesSendBootApplication.java" @@ -0,0 +1,37 @@ +package com.fly; + +import java.net.InetAddress; + +import org.apache.commons.lang3.SystemUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class FilesSendBootApplication +{ + @Value("${server.port}") + String port; + + public static void main(String[] args) + { + SpringApplication.run(FilesSendBootApplication.class, args); + } + + @Bean + @ConditionalOnWebApplication + CommandLineRunner init() + { + return args -> { + if (SystemUtils.IS_OS_WINDOWS)// 防止非windows系统报错,启动失败 + { + String ip = InetAddress.getLocalHost().getHostAddress(); + String url = "http://" + ip + ":" + port; + Runtime.getRuntime().exec("cmd /c start " + url); + } + }; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/auth/AuthInterceptor.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/auth/AuthInterceptor.java" new file mode 100644 index 0000000..5b3b7ce --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/auth/AuthInterceptor.java" @@ -0,0 +1,49 @@ +package com.fly.core.auth; + +import java.nio.charset.StandardCharsets; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fly.filesend.entity.JsonResult; +import com.fly.filesend.service.TokenService; + +/** + * + * AuthInterceptor + * + * @author 00fly + * @version [版本号, 2019年7月21日] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Component +public class AuthInterceptor extends HandlerInterceptorAdapter +{ + @Autowired + TokenService tokenService; + + private ObjectMapper mapper = new ObjectMapper(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception + { + String token = (String)request.getSession().getAttribute("token"); + if (!tokenService.valide(token)) + { + JsonResult result = JsonResult.error("系统登录状态失效,请重新登录"); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(mapper.writeValueAsString(result)); + return false; + } + return true; + } +} \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/Knife4jConfig.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/Knife4jConfig.java" new file mode 100644 index 0000000..0b01668 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/Knife4jConfig.java" @@ -0,0 +1,62 @@ +package com.fly.core.config; + +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; + +import io.swagger.annotations.ApiOperation; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; + +/** + * Knife4jConfig + * + */ +@EnableKnife4j +@Configuration +@EnableSwagger2WebMvc +@ConditionalOnWebApplication +@Import(BeanValidatorPluginsConfiguration.class) +public class Knife4jConfig +{ + /** + * 开发、测试环境接口文档打开 + * + * @return + * @see [类、类#方法、类#成员] + */ + @Bean + Docket createRestApi() + { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) + .enable(true) + .select() + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) // 包下的类,生成接口文档 + .build() + .securitySchemes(security()); + } + + private ApiInfo apiInfo() + { + return new ApiInfoBuilder().title("数据接口API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build(); + } + + private List security() + { + return Collections.singletonList(new ApiKey("token", "token", "header")); + } +} \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/WebMvcConfig.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/WebMvcConfig.java" new file mode 100644 index 0000000..e96bd8e --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/config/WebMvcConfig.java" @@ -0,0 +1,98 @@ +package com.fly.core.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.fly.core.auth.AuthInterceptor; + +/** + * + * mvc配置 + * + * @author 00fly + * @version [版本号, 2021年4月23日] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Configuration +@ConditionalOnWebApplication +public class WebMvcConfig implements WebMvcConfigurer +{ + @Autowired + private AuthInterceptor authInterceptor; + + @Override + public void configureMessageConverters(final List> converters) + { + converters.add(stringHttpMessageConverter()); + converters.add(mappingJackson2HttpMessageConverter()); + } + + @Override + public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) + { + configurer.defaultContentType(MediaType.APPLICATION_JSON); + configurer.ignoreUnknownPathExtensions(false); + configurer.favorPathExtension(true); + configurer.favorParameter(false); + final Map mediaTypes = new ConcurrentHashMap<>(3); + mediaTypes.put("atom", MediaType.APPLICATION_ATOM_XML); + mediaTypes.put("html", MediaType.TEXT_HTML); + mediaTypes.put("json", MediaType.APPLICATION_JSON); + configurer.mediaTypes(mediaTypes); + } + + @Bean + StringHttpMessageConverter stringHttpMessageConverter() + { + return new StringHttpMessageConverter(); + } + + @Bean + MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() + { + final MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); + final List list = new ArrayList<>(); + list.add(MediaType.APPLICATION_JSON); + list.add(MediaType.APPLICATION_XML); + list.add(MediaType.TEXT_PLAIN); + list.add(MediaType.TEXT_HTML); + list.add(MediaType.TEXT_XML); + messageConverter.setSupportedMediaTypes(list); + return messageConverter; + } + + /** + * 等价于mvc中
+ * 等价于mvc中 + * + * @param registry + */ + @Override + public void addViewControllers(final ViewControllerRegistry registry) + { + // registry.addViewController("/").setViewName("redirect:index"); + // registry.addViewController("/index").setViewName("index.html"); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(authInterceptor).addPathPatterns("/rest/file/**", "/file/**"); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/GlobalExceptionHandler.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/GlobalExceptionHandler.java" new file mode 100644 index 0000000..529fe06 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/GlobalExceptionHandler.java" @@ -0,0 +1,63 @@ +package com.fly.core.exception; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.fly.filesend.entity.JsonResult; + +import lombok.extern.slf4j.Slf4j; + +/** + * 统一异常处理器 + * + * @author 00fly + * @version [版本号, 2018-09-11] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler +{ + @ExceptionHandler(value = Exception.class) + public JsonResult handleBadRequest(Exception exception) + { + // JSR303参数校验异常 + if (exception instanceof BindException) + { + BindingResult bindingResult = ((BindException)exception).getBindingResult(); + if (null != bindingResult && bindingResult.hasErrors()) + { + List errMsg = new ArrayList<>(); + bindingResult.getFieldErrors().stream().forEach(fieldError -> { + errMsg.add(fieldError.getDefaultMessage()); + }); + Collections.sort(errMsg); + return JsonResult.error(StringUtils.join(errMsg, ",")); + } + } + if (exception instanceof MethodArgumentNotValidException) + { + BindingResult bindingResult = ((MethodArgumentNotValidException)exception).getBindingResult(); + if (null != bindingResult && bindingResult.hasErrors()) + { + List errMsg = new ArrayList<>(); + bindingResult.getFieldErrors().stream().forEach(fieldError -> { + errMsg.add(fieldError.getDefaultMessage()); + }); + return JsonResult.error(StringUtils.join(errMsg, ",")); + } + } + // 其余情况 + log.error("Error: handleBadRequest StackTrace : {}", exception); + return JsonResult.error(StringUtils.defaultString(exception.getMessage(), "系统异常,请联系管理员")); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/ValidateException.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/ValidateException.java" new file mode 100644 index 0000000..9d6c15f --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/exception/ValidateException.java" @@ -0,0 +1,16 @@ +package com.fly.core.exception; + +public class ValidateException extends RuntimeException +{ + private static final long serialVersionUID = -939208231165751812L; + + public ValidateException() + { + super(); + } + + public ValidateException(String message) + { + super(message); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/BufferedImageLuminanceSource.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/BufferedImageLuminanceSource.java" new file mode 100644 index 0000000..67c67d8 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/BufferedImageLuminanceSource.java" @@ -0,0 +1,108 @@ +package com.fly.core.qr; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; + +import com.google.zxing.LuminanceSource; + +public class BufferedImageLuminanceSource extends LuminanceSource +{ + private BufferedImage image; + + private int left; + + private int top; + + public BufferedImageLuminanceSource(BufferedImage image) + { + this(image, 0, 0, image.getWidth(), image.getHeight()); + } + + public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) + { + super(width, height); + + int sourceWidth = image.getWidth(); + int sourceHeight = image.getHeight(); + if (left + width > sourceWidth || top + height > sourceHeight) + { + throw new IllegalArgumentException("Crop rectangle does not fit within image data."); + } + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + if ((image.getRGB(x, y) & 0xFF000000) == 0) + { + image.setRGB(x, y, 0xFFFFFFFF); + } + } + } + + this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY); + this.image.getGraphics().drawImage(image, 0, 0, null); + this.left = left; + this.top = top; + } + + @Override + public byte[] getRow(int y, byte[] row) + { + if (y < 0 || y >= getHeight()) + { + throw new IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + if (row == null || row.length < width) + { + row = new byte[width]; + } + image.getRaster().getDataElements(left, top + y, width, 1, row); + return row; + } + + @Override + public byte[] getMatrix() + { + int width = getWidth(); + int height = getHeight(); + int area = width * height; + byte[] matrix = new byte[area]; + image.getRaster().getDataElements(left, top, width, height, matrix); + return matrix; + } + + @Override + public boolean isCropSupported() + { + return true; + } + + @Override + public LuminanceSource crop(int left, int top, int width, int height) + { + return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height); + } + + @Override + public boolean isRotateSupported() + { + return true; + } + + @Override + public LuminanceSource rotateCounterClockwise() + { + int sourceWidth = image.getWidth(); + int sourceHeight = image.getHeight(); + AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth); + BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = rotatedImage.createGraphics(); + g.drawImage(image, transform, null); + g.dispose(); + int width = getWidth(); + return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width); + } +} \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/QRCodeUtil.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/QRCodeUtil.java" new file mode 100644 index 0000000..78ac279 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/core/qr/QRCodeUtil.java" @@ -0,0 +1,153 @@ +package com.fly.core.qr; + +import java.awt.BasicStroke; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Shape; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Hashtable; + +import javax.imageio.ImageIO; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.Result; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; + +public class QRCodeUtil +{ + /** + * 二维码尺寸 + */ + private static final int QRCODE_SIZE = 300; + + /** + * LOGO宽度 + */ + private static final int WIDTH = 60; + + /** + * LOGO高度 + */ + private static final int HEIGHT = 60; + + /** + * 给定内容、图标生成二维码图片 + * + * @param content 內容 + * @param imgURL 图标 + * @param needCompress 是否压缩尺寸 + * @return + * @throws Exception + * @see [类、类#方法、类#成员] + */ + public static BufferedImage createImage(String content, URL imgURL, boolean needCompress) + throws Exception + { + Hashtable hints = new Hashtable<>(); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8); + hints.put(EncodeHintType.MARGIN, 1); + BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); + int width = bitMatrix.getWidth(); + int height = bitMatrix.getHeight(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); + } + } + if (imgURL == null) + { + return image; + } + // 插入图片 + insertImage(image, imgURL, needCompress); + return image; + } + + private static void insertImage(BufferedImage source, URL imgURL, boolean needCompress) + throws Exception + { + if (imgURL == null) + { + System.err.println("文件不存在!"); + return; + } + Image src = ImageIO.read(imgURL); + int width = src.getWidth(null); + int height = src.getHeight(null); + if (needCompress) + { + // 压缩LOGO + width = Math.min(width, WIDTH); + height = Math.min(height, HEIGHT); + Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH); + BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics g = tag.getGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + src = image; + } + // 插入LOGO + Graphics2D graph = source.createGraphics(); + int x = (QRCODE_SIZE - width) / 2; + int y = (QRCODE_SIZE - height) / 2; + graph.drawImage(src, x, y, width, height, null); + Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6); + graph.setStroke(new BasicStroke(3f)); + graph.draw(shape); + graph.dispose(); + } + + /** + * 解析二维码图 + * + * @param file + * @return + * @throws Exception + * @see [类、类#方法、类#成员] + */ + public static String decode(File file) + throws Exception + { + BufferedImage image = ImageIO.read(file); + if (image == null) + { + return null; + } + BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + Hashtable hints = new Hashtable<>(); + hints.put(DecodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name()); + Result result = new MultiFormatReader().decode(bitmap, hints); + return result.getText(); + } + + /** + * 解析二维码图 + * + * @param path + * @return + * @throws Exception + * @see [类、类#方法、类#成员] + */ + public static String decode(String path) + throws Exception + { + return decode(new File(path)); + } +} \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/ApiController.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/ApiController.java" new file mode 100644 index 0000000..146cc99 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/ApiController.java" @@ -0,0 +1,84 @@ +package com.fly.filesend.controller; + +import java.awt.image.BufferedImage; +import java.net.URL; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.fly.core.qr.QRCodeUtil; +import com.fly.filesend.entity.JsonResult; +import com.fly.filesend.service.TokenService; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; + +@Api(tags = "系统接口") +@RestController +@RequestMapping("/api") +public class ApiController +{ + @Autowired + HttpSession httpSession; + + @Autowired + TokenService tokenService; + + @ApiOperationSupport(order = 10) + @PostMapping("/login") + @ApiOperation("登录系统") + public JsonResult login(String token) + { + if (!tokenService.valide(token)) + { + return JsonResult.error("token empty or valide failed!"); + } + httpSession.setAttribute("token", token); + String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"); + return JsonResult.success(date + " login success!"); + } + + @ApiOperationSupport(order = 20) + @PostMapping("/logout") + @ApiOperation("退出系统") + public JsonResult logout() + { + httpSession.invalidate(); + String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"); + return JsonResult.success(date + " logout success!"); + } + + @ApiOperation("生成二维码") + @ApiImplicitParam(name = "content", value = "二维码文本", required = true, example = "乡愁是一棵没有年轮的树,永不老去") + @PostMapping(value = "/qr/create", produces = MediaType.IMAGE_JPEG_VALUE) + public void index(String content, HttpServletResponse response) + throws Exception + { + BufferedImage image; + if (StringUtils.isNotBlank(content)) + { + Resource resource = new ClassPathResource("img/dog.jpg"); + URL imgURL = resource.getURL(); + image = QRCodeUtil.createImage(content, imgURL, true); + } + else + { + image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB); + } + // 输出图象到页面 + ImageIO.write(image, "JPEG", response.getOutputStream()); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/IndexController.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/IndexController.java" new file mode 100644 index 0000000..e72047d --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/IndexController.java" @@ -0,0 +1,69 @@ +package com.fly.filesend.controller; + +import java.io.File; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpSession; + +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import com.fly.filesend.service.TextService; +import com.fly.filesend.service.TokenService; + +@Controller +public class IndexController +{ + @Autowired + HttpSession httpSession; + + File root = new File("upload"); + + @Autowired + TokenService tokenService; + + @Autowired + TextService textService; + + @GetMapping({"/", "/index"}) + public String index(Model model) + { + String token = (String)httpSession.getAttribute("token"); + if (tokenService.valide(token)) + { + model.addAttribute("text", textService.getText()); + model.addAttribute("isLogin", true); + model.addAttribute("sysTokenTime", tokenService.getTokenTime()); + if (root.exists()) + { + List files = FileUtils.listFiles(root, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList()); + model.addAttribute("files", files); + model.addAttribute("size", FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(root))); + } + } + return "index"; + } + + @PostMapping("/login") + public String login(String token) + { + if (tokenService.valide(token)) + { + httpSession.setAttribute("token", token); + } + return "redirect:/index"; + } + + @GetMapping("/logout") + public String logout() + { + httpSession.invalidate(); + return "redirect:/index"; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/FileController.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/FileController.java" new file mode 100644 index 0000000..60014fb --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/FileController.java" @@ -0,0 +1,103 @@ +package com.fly.filesend.controller.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import com.fly.core.exception.ValidateException; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Controller +@RequestMapping("/file") +public class FileController +{ + File root = new File("upload"); + + @PostMapping("/upload") + public String upload(@RequestParam MultipartFile[] files) + throws IOException + { + if (files == null || files.length == 0) + { + throw new ValidateException("files is null"); + } + String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd"); + String dir = root.getCanonicalPath() + File.separator + date + File.separator; + new File(dir).mkdirs(); + + // 保存文件 + Arrays.stream(files).filter(file -> StringUtils.isNotBlank(file.getOriginalFilename())).forEach(file -> { + try + { + File newFile = new File(dir + file.getOriginalFilename()); + FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(newFile)); + log.info("###### file upload to: {}", dir); + } + catch (IOException e) + { + log.error(e.getMessage(), e); + } + }); + return "redirect:/index"; + } + + @GetMapping(value = "/down/{index}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public void down(@PathVariable int index, HttpServletResponse response) + throws IOException + { + List files = FileUtils.listFiles(root, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList()); + if (index >= 0 && index < files.size()) + { + File file = files.get(index); + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.name())); + response.setHeader("Cache-Control", "no-store, no-cache"); + FileCopyUtils.copy(new FileInputStream(file), response.getOutputStream()); + } + } + + @GetMapping(value = "/clear") + public String clear() + throws IOException + { + if (root.exists()) + { + FileUtils.cleanDirectory(root); + } + return "redirect:/index"; + } + + @GetMapping(value = "/delete/{index}") + public String delete(@PathVariable int index) + { + List files = FileUtils.listFiles(root, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList()); + if (index >= 0 && index < files.size()) + { + files.get(index).delete(); + } + return "redirect:/index"; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/RestFileController.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/RestFileController.java" new file mode 100644 index 0000000..083d927 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/RestFileController.java" @@ -0,0 +1,106 @@ +package com.fly.filesend.controller.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.http.MediaType; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.fly.core.exception.ValidateException; +import com.fly.filesend.entity.JsonResult; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Api(tags = "文件上传、下载接口") +@RestController +@RequestMapping("/rest/file") +public class RestFileController +{ + File root = new File("upload"); + + @ApiOperation("文件下载, index取值 [0, files.length)") + @ApiImplicitParam(name = "index", value = "文件索引,起始值0", required = true, allowableValues = "0,1,2,3,4,5,6,7,8,9,10") + @GetMapping(value = "/down/{index}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public void down(@PathVariable int index, HttpServletResponse response) + throws IOException + { + List files = FileUtils.listFiles(root, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList()); + if (index >= 0 && index < files.size()) + { + File file = files.get(index); + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.name())); + response.setHeader("Cache-Control", "no-store, no-cache"); + FileCopyUtils.copy(new FileInputStream(file), response.getOutputStream()); + } + } + + @ApiOperation("文件搜索") + @PostMapping("/list") + public JsonResult list() + { + if (!root.exists() || !root.isDirectory()) + { + return JsonResult.error("文件目录不存在"); + } + // 检索文件路径排序 + List paths = FileUtils.listFiles(root, null, true).stream().filter(f -> f.isFile()).map(f -> f.getPath()).sorted().collect(Collectors.toList()); + return JsonResult.success(paths); + } + + @ApiOperation("文件批量上传处理") + @PostMapping("/upload") + public JsonResult upload(MultipartFile[] files) + throws IOException + { + if (files == null || files.length == 0) + { + throw new ValidateException("文件不能为空"); + } + String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd"); + String dir = root.getCanonicalPath() + File.separator + date + File.separator; + new File(dir).mkdirs(); + + // 保存文件 + Arrays.stream(files).filter(file -> StringUtils.isNotBlank(file.getOriginalFilename())).forEach(file -> { + try + { + File newFile = new File(dir + file.getOriginalFilename()); + FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(newFile)); + log.info("###### file upload to: {}", dir); + } + catch (IOException e) + { + log.error(e.getMessage(), e); + } + }); + if (SystemUtils.IS_OS_WINDOWS) + { + Runtime.getRuntime().exec("cmd /c start " + dir); + } + return JsonResult.success("文件上传成功,保存目录:" + dir); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/TextController.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/TextController.java" new file mode 100644 index 0000000..77e0a5d --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/controller/file/TextController.java" @@ -0,0 +1,23 @@ +package com.fly.filesend.controller.file; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.fly.filesend.service.TextService; + +@Controller +@RequestMapping("/text") +public class TextController +{ + @Autowired + TextService textService; + + @PostMapping("/submit") + public String upload(String text) + { + textService.setText(text); + return "redirect:/index"; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/entity/JsonResult.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/entity/JsonResult.java" new file mode 100644 index 0000000..4e102b7 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/entity/JsonResult.java" @@ -0,0 +1,65 @@ +package com.fly.filesend.entity; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * + * 结果对象 + * + * @author 00fly + * @version [版本号, 2021年5月2日] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Data +@ApiModel(description = "Json格式消息体") +public class JsonResult +{ + @ApiModelProperty(value = "数据对象") + private T data; + + @ApiModelProperty(value = "是否成功", required = true, example = "true") + private boolean success; + + @ApiModelProperty(value = "错误码") + private String errorCode; + + @ApiModelProperty(value = "提示信息") + private String message; + + public JsonResult() + { + super(); + } + + public static JsonResult success(T data) + { + JsonResult r = new JsonResult<>(); + r.setData(data); + r.setSuccess(true); + return r; + } + + public static JsonResult success() + { + JsonResult r = new JsonResult<>(); + r.setSuccess(true); + return r; + } + + public static JsonResult error(String code, String msg) + { + JsonResult r = new JsonResult<>(); + r.setSuccess(false); + r.setErrorCode(code); + r.setMessage(msg); + return r; + } + + public static JsonResult error(String msg) + { + return error("500", msg); + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService.java" new file mode 100644 index 0000000..bf4d4e6 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService.java" @@ -0,0 +1,20 @@ +package com.fly.filesend.service; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +@Service +public class TextService +{ + private String text; + + public String getText() + { + return StringUtils.trimToEmpty(text); + } + + public void setText(String text) + { + this.text = text; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService2.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService2.java" new file mode 100644 index 0000000..0079dc0 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TextService2.java" @@ -0,0 +1,50 @@ +package com.fly.filesend.service; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListMap; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.stereotype.Service; + +@Service +public class TextService2 +{ + Map map = new ConcurrentSkipListMap<>(); + + /** + * 时间戳keys + * + * @return + */ + public Set getKeys() + { + return map.keySet(); + } + + /** + * 获取值 + * + * @param key + * @return + */ + public String getText(String key) + { + return StringUtils.trimToEmpty(map.get(key)); + } + + public void setText(String text) + { + if (StringUtils.isNotBlank(text)) + { + String key = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss.SSS"); + map.put(key, text); + while (map.size() > 10)// 保留10次历史 + { + String firstKey = map.keySet().stream().findFirst().get(); + map.remove(firstKey); + } + } + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TokenService.java" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TokenService.java" new file mode 100644 index 0000000..1f789f9 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/java/com/fly/filesend/service/TokenService.java" @@ -0,0 +1,84 @@ +package com.fly.filesend.service; + +import java.util.Date; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class TokenService +{ + private String sysToken; + + /** + * sysToken有效时间 + */ + private Date sysTokenTime; + + private String sysTokenTimeShow; + + /** + * sysToken有效小时数(default:2) + */ + @Value("${token.valide.hours:2}") + private Integer hours; + + @Value("${system.auto.login:false}") + private Boolean autoLogin; + + /** + * 验证token是否合法 + * + * @param token + * @return + * @return + */ + public boolean valide(String token) + { + if (autoLogin) + { + return true; + } + boolean success = StringUtils.equals(token, getToken()); + if (!success) + { + log.info("------ now valid sysToken is: {}", sysToken); + } + return success; + } + + /** + * 获取sysToken有效时间 + * + * @return + */ + public String getTokenTime() + { + return sysTokenTimeShow; + } + + /** + * 获取sysToken + * + * @return + * @see [类、类#方法、类#成员] + */ + private String getToken() + { + Date now = new Date(); + if (sysTokenTime == null || now.after(sysTokenTime)) + { + sysToken = UUID.randomUUID().toString().replace("-", ""); + sysTokenTime = DateUtils.addHours(now, hours); + sysTokenTimeShow = DateFormatUtils.format(sysTokenTime, "yyyy-MM-dd HH:mm:ss"); + } + return sysToken; + } +} diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-dev.yml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-dev.yml" new file mode 100644 index 0000000..48ac8c9 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-dev.yml" @@ -0,0 +1,16 @@ +spring: + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + thymeleaf: + cache: false + check-template-location: true + encoding: UTF-8 + mode: HTML + prefix: classpath:/templates/ + suffix: .html + +logging: + level: + root: info diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-prod.yml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-prod.yml" new file mode 100644 index 0000000..f6cdb60 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-prod.yml" @@ -0,0 +1,16 @@ +spring: + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + thymeleaf: + cache: true + check-template-location: true + encoding: UTF-8 + mode: HTML + prefix: classpath:/templates/ + suffix: .html + +logging: + level: + root: info diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-test.yml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-test.yml" new file mode 100644 index 0000000..c1a4071 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application-test.yml" @@ -0,0 +1,16 @@ +spring: + servlet: + multipart: + max-file-size: 1000MB + max-request-size: 1000MB + thymeleaf: + cache: true + check-template-location: true + encoding: UTF-8 + mode: HTML + prefix: classpath:/templates/ + suffix: .html + +logging: + level: + root: info diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application.yml" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application.yml" new file mode 100644 index 0000000..c398aa3 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/application.yml" @@ -0,0 +1,13 @@ +server: + port: 8080 + servlet: + context-path: / + session: + timeout: 1800 #以秒为单位 + tomcat: + max-http-form-post-size: 200MB #post表单请求体数据限制,默认2MB + max-swallow-size: 200MB #内嵌tomcat最大请求吞吐量限制,默认2MB +spring: + profiles: + active: + - dev diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/img/dog.jpg" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/img/dog.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..8c503cd2867cc6eb7cfd9fd8853fc0bbc9389236 GIT binary patch literal 28309 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�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 + + + + + + 404(找不到页面) + + + + + + + + + + +
+
+

404!

+

很抱歉,没有找到这个页面!

+

+ 返回首页 +
+
+ + + \ No newline at end of file diff --git "a/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/templates/index.html" "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/templates/index.html" new file mode 100644 index 0000000..e61c864 --- /dev/null +++ "b/00fly-Web\346\226\207\344\273\266\345\217\212\346\226\207\346\234\254\345\205\261\344\272\253\345\267\245\345\205\267/src/main/resources/templates/index.html" @@ -0,0 +1,138 @@ + + + + + + + + + +simple文件共享工具 + + +
+ + + + + + + + + +
Navigate
index出错页面doc.html
+
+
+
+ + + + + + + + + + + + + +
功能列表
+
+ + token + + +
+
+ + 退出登录 | 清空文件 +
+
+ 批量上传,单个文件小于10MB,单个请求小于100MB + + +
+
+
+

+
+ + + + + + + + + +
+
+ + + 删除
+
+ + + + + +
文本
+
+