Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fileserver #26

Merged
merged 9 commits into from
Oct 22, 2024
1 change: 1 addition & 0 deletions muyun-boot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
implementation(project(":muyun-platform"))
implementation(project(":muyun-proxy"))
implementation(project(":muyun-log"))
implementation(project(":muyun-fileserver"))

testImplementation("io.quarkus:quarkus-junit5")
testImplementation("io.rest-assured:rest-assured")
Expand Down
6 changes: 4 additions & 2 deletions muyun-boot/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
muyun:
debug: true
super-user-id: 1
fileserver:
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
upload-path: D:\\file-uploads\\
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
web:
redirects:
- from: /
Expand All @@ -23,10 +25,10 @@ quarkus:
datasource:
db-kind: postgresql
username: postgres
password: muyun2024
password: postgres
# password: Muyun20&24
jdbc:
url: jdbc:postgresql://localhost:54324/muyun
url: jdbc:postgresql://localhost:5432/muyun
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
# url: jdbc:postgresql://192.168.6.205:32128/uni_antifraud
# enabled: true
# reactive:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package net.ximatai.muyun.test.fileserver;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;
import io.vertx.core.json.JsonObject;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;

@QuarkusTest
public class TestFileserverRouter {

// 文件名
String fileName;
String uid;
// 文件内容
String fileContent = "";
// 临时文件
File tempFile;

@BeforeEach
public void setup() throws IOException {
int fileNameInt = getRandomInt();
fileName = String.valueOf(fileNameInt);
try {
tempFile = File.createTempFile(fileName, ".txt");
FileOutputStream fos = new FileOutputStream(tempFile);
int ctx1 = getRandomInt();
fileContent += String.valueOf(ctx1 + "\n");
int ctx2 = getRandomInt();
fileContent += String.valueOf(ctx2);
fos.write(fileContent.getBytes());
fos.close();
} catch (IOException e) {
throw(e);
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
}
}

@Test
void testFileProcess() {
// 文件上传
Response response = given()
.multiPart("file", tempFile)
.when()
.post("/fileserver/form")
.then()
.log().all()
.statusCode(200)
.extract()
.response();
// String jsonResponse = response.getBody().asString();
// JsonObject jsonObject = new JsonObject(jsonResponse);

String uid = response.getBody().asString();

// 下载文件
Response response2 = given()
.when()
.get("/fileserver/download/" + uid)
.then()
.log().all()
.statusCode(200)
.extract()
.response();

String downloadContent = response2.getBody().asString();
// 验证文件内容是否相同
assertEquals(fileContent, downloadContent);

// 读取文件info
Response response3 = given()
.when()
.get("/fileserver/info/" + uid)
.then()
.log().all()
.statusCode(200)
.extract()
.response();
String jsonResponse = response3.getBody().asString();
JsonObject jsonObject = new JsonObject(jsonResponse);
String fileName = jsonObject.getString("name");
assertEquals(tempFile.getName(), fileName);
}

@AfterEach
public void tearDown() {
String deleteUrl = "/fileserver/delete/" + uid;
given()
.when()
.get(deleteUrl)
.then()
.statusCode(200);

tempFile.delete();
}

/**
* @return 返回一个随机的正数
*/
int getRandomInt() {
Random rand = new Random();
int num = rand.nextInt();
while (num < 1000000000) {
num = rand.nextInt();
}
return num;
}
}
12 changes: 12 additions & 0 deletions muyun-fileserver/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
java
`java-library`
}

dependencies {
api(enforcedPlatform(libs.quarkus.platform.bom))
api("io.quarkus:quarkus-rest")
api("io.quarkus:quarkus-arc")
api("io.quarkus:quarkus-vertx")
api("io.quarkus:quarkus-reactive-routes")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.ximatai.muyun.fileserver;

import io.smallrye.config.ConfigMapping;

@ConfigMapping(prefix = "fileserver")
public interface FileserverConfig {
String uploadPath();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.ximatai.muyun.fileserver;

import io.quarkus.runtime.Startup;
import io.quarkus.vertx.web.RouteFilter;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@Startup
@ApplicationScoped
public class FileserverRegister {

@Inject
FileserverConfig config;

@RouteFilter(10) // 该注解中有route
void filter(RoutingContext context) {
BodyHandler.create().setUploadsDirectory(config.uploadPath()).handle(context);
// BodyHandler是一个类,对象handler可以作为route的参数
// create()函数返回一个BodyHandlerImpl
// BodyHandlerImpl中有handle方法
// handle方法接收context来处理
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package net.ximatai.muyun.fileserver;

import io.quarkus.vertx.web.Route;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.RoutingContext;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.Vertx;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;

public class FileserverRouter {

final Logger logger = LoggerFactory.getLogger(getClass());

String originalFileName;

@Inject
FileserverConfig config;

@Inject
Vertx vertx;

@Route(path = "/fileserver/index", methods = Route.HttpMethod.GET)
public void indexFunc(RoutingContext ctx) {
ctx.response()
.putHeader("content-type", "text/html")
.end(
"<form action=\"/fs/form\" method=\"post\" enctype=\"multipart/form-data\">\n"
+ " <div>\n"
+ " <label for=\"name\">Select a file:</label>\n"
+ " <input type=\"file\" name=\"file\" />\n"
+ " </div>\n"
+ " <div class=\"button\">\n"
+ " <button type=\"submit\">Send</button>\n"
+ " </div>"
+ "</form>"
);
}

@Route(path = "/fileserver/form", methods = Route.HttpMethod.POST)
public void form(RoutingContext ctx) {

// 支持分块传输编码
ctx.response().setChunked(true);
for (FileUpload f : ctx.fileUploads()) {
// 获取文件名、文件大小、uid
String uploadedFileName = f.uploadedFileName();
originalFileName = f.fileName();
long fileSize = f.size();
String uid = "bsy-" + uploadedFileName.split("\\\\")[2];
String fileNameUid = suffixN(uid);
String fileContextUid = suffixO(uid);
vertx.fileSystem().writeFile(config.uploadPath() + fileNameUid, Buffer.buffer(originalFileName));
vertx.fileSystem().copy(uploadedFileName, config.uploadPath() + fileContextUid);
vertx.fileSystem().delete(uploadedFileName);
ctx.response().write(uid);
}
ctx.response().end();
}

@Route(path = "/fileserver/download/:fileUid", methods = Route.HttpMethod.GET)
public void download(RoutingContext ctx) {
String fileUid = ctx.pathParam("fileUid");
String nameFile = suffixN(fileUid);
String contentFile = suffixO(fileUid);
String contentFilePath = config.uploadPath() + contentFile;
File file = new File(contentFilePath);
String nameFilePath = config.uploadPath() + nameFile;
vertx.fileSystem().readFile(nameFilePath, result -> {
if (result.succeeded()) {
Buffer buffer = result.result();
String content = buffer.toString("UTF-8");
if (file.exists()) {
ctx.response()
.putHeader("Content-Disposition", "attachment; filename=" + content)
.sendFile(file.getPath());
}
} else {
logger.error("Failed to read file name: " + result.cause());
ctx.fail(result.cause());
}
});
}

@Route(path = "/fileserver/delete/:uid", methods = Route.HttpMethod.GET)
public void delete(RoutingContext ctx) {
String uid = ctx.pathParam("uid");
String deleteNamePath = suffixN(config.uploadPath() + uid);
String deleteContentPath = suffixO(config.uploadPath() + uid);
File fileN = new File(deleteNamePath);
File fileO = new File(deleteContentPath);
boolean isDelete1 = fileN.delete();
boolean isDelete2 = fileO.delete();
ctx.response().end("Successfully deleted.");
}

@Route(path = "/fileserver/info/:uid", methods = Route.HttpMethod.GET)
public void info(RoutingContext ctx) {
String uid = ctx.pathParam("uid");
String fileNamePath = suffixN(config.uploadPath() + uid);
String fileContentPath = suffixO(config.uploadPath() + uid);
File file = new File(fileNamePath);
File fileContent = new File(fileContentPath);
vertx.fileSystem().readFile(fileNamePath, result -> {
if (result.succeeded()) {
Buffer buffer = result.result();
String line = buffer.toString("UTF-8");
String suffix = line.split("\\.")[1];
Path path = Paths.get(fileNamePath);
String createTime = "00:00";
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
FileTime creationTime = attrs.creationTime();
createTime = creationTime.toString();
} catch (IOException e) {
logger.error("Failed to read file attributes", e);
}

JsonObject jsonObject = new JsonObject()
.put("name", line)
.put("size", fileContent.length())
.put("suffix", suffix)
.put("uid", uid)
.put("time", createTime);
ctx.response()
.putHeader("Content-Type", "application/json")
.end(jsonObject.toString());

} else {
logger.error("Failed to read file name: " + result.cause());
ctx.fail(result.cause());
}
});
}

// uid文件名处理方法
public String suffixN(String fileName) {
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
return fileName + "-n";
}

public String suffixO(String fileName) {
Ning19230223 marked this conversation as resolved.
Show resolved Hide resolved
return fileName + "-o";
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ include("muyun-platform")
include("muyun-authorization")
include("muyun-proxy")
include("muyun-log")
include("muyun-fileserver")

include("muyun-boot")

Expand Down