tio-boot 案例 - 文件上传和下载
添加依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<tio-boot.version>1.8.7</tio-boot.version>
<lombok-version>1.18.30</lombok-version>
<main.class>com.litongjava.tio.boot.sqllite.App</main.class>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--https://central.sonatype.com/artifact/com.litongjava/ApiTable-->
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>ApiTable</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!-- sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>tio-boot</artifactId>
<version>${tio-boot.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>hotswap-classloader</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
<profiles>
<!-- 开发环境 -->
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 生产环境 -->
<profile>
<id>production</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.4</version>
<configuration>
<mainClass>${main.class}</mainClass>
<excludeGroupIds>org.projectlombok</excludeGroupIds>
</configuration>
<!-- 设置执行目标 -->
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
数据表
创建一张数据表,保持上传的数据
CREATE TABLE "sys_upload_file" (
"id" text NOT NULL,
"saveFolder" TEXT NOT NULL,
"suffixName" TEXT NOT NULL,
"filename" TEXT NOT NULL,
PRIMARY KEY ("id")
);
配置文件
app.properties
#jdbc-sqlliste
jdbc.driverClass=org.sqlite.JDBC
jdbc.url=jdbc:sqlite:D:/sqllite/student.db
jdbc.user=
jdbc.pswd=
jdbc.showSql=true
jdbc.validationQuery=select 1
启动类
package com.litongjava.tio.boo.demo.file;
import com.litongjava.hotswap.wrapper.tio.boot.TioApplicationWrapper;
import com.litongjava.jfinal.aop.annotation.AComponentScan;
@AComponentScan
public class TioBootFileApp {
public static void main(String[] args) {
long start = System.currentTimeMillis();
TioApplicationWrapper.run(TioBootFileApp.class, args);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
配置类
package com.litongjava.tio.boo.demo.file.config;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.jfinal.template.Engine;
import com.jfinal.template.source.ClassPathSourceFactory;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.litongjava.jfinal.plugin.activerecord.OrderedFieldContainerFactory;
import com.litongjava.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.litongjava.jfinal.plugin.hikaricp.DsContainer;
import com.litongjava.tio.boot.constatns.ConfigKeys;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.utils.environment.EnvUtils;
@AConfiguration
public class DbConfig {
/**
* create datasource
* @return
*/
@Initialization(priority = 10)
public DataSource dataSource() {
// get parameter from config file
String jdbcUrl = EnvUtils.get("jdbc.url");
String jdbcUser = EnvUtils.get("jdbc.user");
String jdbcPswd = EnvUtils.get("jdbc.pswd");
String jdbcValidationQuery = EnvUtils.get("jdbc.validationQuery");
// create datasource
DruidDataSource druidDataSource = new DruidDataSource();
// set basic parameter
druidDataSource.setUrl(jdbcUrl);
druidDataSource.setUsername(jdbcUser);
druidDataSource.setPassword(jdbcPswd);
druidDataSource.setValidationQuery(jdbcValidationQuery);
// save datasource
DsContainer.setDataSource(druidDataSource);
// close datasource while server close
TioBootServer.me().addDestroyMethod(druidDataSource::close);
return druidDataSource;
}
/**
* create ActiveRecordPlugin
* @return
* @throws Exception
*/
@Initialization
public ActiveRecordPlugin activeRecordPlugin() throws Exception {
// get datasource from DsContainer
DataSource dataSource = DsContainer.ds;
// get parameter from config file
Boolean tioDevMode = EnvUtils.getBoolean(ConfigKeys.TIO_DEV_MODE, false);
boolean jdbcShowSql = EnvUtils.getBoolean("jdbc.showSql", false);
// cretae plugin
ActiveRecordPlugin arp = new ActiveRecordPlugin(dataSource);
// set parameter
arp.setDialect(new Sqlite3Dialect());
arp.setContainerFactory(new OrderedFieldContainerFactory());
arp.setShowSql(jdbcShowSql);
if (tioDevMode) {
arp.setDevMode(true);
}
// config engine
Engine engine = arp.getEngine();
engine.setSourceFactory(new ClassPathSourceFactory());
engine.setCompressorOn(' ');
engine.setCompressorOn('\n');
// arp.addSqlTemplate("/sql/all_sqls.sql");
// start plugin
arp.start();
// close plugin while server close
TioBootServer.me().addDestroyMethod(arp::stop);
return arp;
}
}
实体类
package com.litongjava.tio.boo.demo.file.model;
import java.io.File;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileSaveResult {
private String id;
private String saveFolder;
private String suffixName;
private String filename;
private File file;
}
常量类
- UPlOAD_FOLDER_NAME 保持文件上传路径
package com.litongjava.tio.boo.demo.file.constants;
public interface AppConstants {
String UPlOAD_FOLDER_NAME = "upload";
}
service
package com.litongjava.tio.boo.demo.file.services;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import com.litongjava.data.services.DbJsonService;
import com.litongjava.jfinal.aop.annotation.AAutowired;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.tio.boo.demo.file.constants.AppConstants;
import com.litongjava.tio.boo.demo.file.model.FileSaveResult;
import com.litongjava.tio.utils.hutool.FileUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FileService {
@AAutowired
private DbJsonService dbJsonService;
public FileSaveResult save(String filename, byte[] fileData) {
String newFilename = UUID.randomUUID().toString();
FileSaveResult fileSaveResult = new FileSaveResult();
fileSaveResult.setId(newFilename);
fileSaveResult.setFilename(filename);
int lastIndexOf = filename.lastIndexOf(".");
if (lastIndexOf > 0) {
String substring = filename.substring(lastIndexOf);
fileSaveResult.setSuffixName(substring);
newFilename += substring;
}
// create dirs
File uploadFolderFile = new File(AppConstants.UPlOAD_FOLDER_NAME);
if (!uploadFolderFile.exists()) {
uploadFolderFile.mkdirs();
log.info("crate upload dir:{}", uploadFolderFile.getAbsolutePath());
}
newFilename = AppConstants.UPlOAD_FOLDER_NAME + "/" + newFilename;
fileSaveResult.setSaveFolder(AppConstants.UPlOAD_FOLDER_NAME);
log.info("newFilename:{}", newFilename);
// save to folder
File file = new File(newFilename);
try {
FileUtil.writeBytes(fileData, file);
fileSaveResult.setFile(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
// save to db
// vresion 1
Record record = new Record();
record.set("id", fileSaveResult.getId());
record.set("saveFolder", fileSaveResult.getSaveFolder());
record.set("suffixName", fileSaveResult.getSuffixName());
record.set("filename", filename);
// version 2 test fail
// Record record = Record.fromBean(fileSaveResult);
boolean save = Db.save("sys_upload_file", record);
if (save) {
return fileSaveResult;
} else {
log.error("save file error:{}", filename);
}
return fileSaveResult;
}
public File getUploadFile(FileSaveResult result) {
String filename = result.getSaveFolder() + "/" + result.getId() + result.getSuffixName();
return new File(filename);
}
public File getUploadFile(String id) {
Record record = Db.findById("sys_upload_file", id);
FileSaveResult fileSaveResult = record.toBean(FileSaveResult.class);
log.info("select from db:{}",fileSaveResult);
return getUploadFile(fileSaveResult);
}
}
controller
package com.litongjava.tio.boo.demo.file.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import com.litongjava.jfinal.aop.annotation.AAutowired;
import com.litongjava.tio.boo.demo.file.model.FileSaveResult;
import com.litongjava.tio.boo.demo.file.services.FileService;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.annotation.RequestPath;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.resp.RespBodyVo;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/file")
@Slf4j
public class FileController {
@AAutowired
private FileService fileService;
public String index() {
return "index";
}
/**
* upload file
* @param file
* @return
*/
public RespBodyVo upload(UploadFile file) {
if (file != null) {
String name = file.getName();
byte[] fileData = file.getData();
// save file
FileSaveResult fileSaveResult = fileService.save(name, fileData);
fileSaveResult.setFile(null);
log.info("save file finished");
return RespBodyVo.ok(fileSaveResult);
}
return RespBodyVo.fail("fail").code(-1);
}
@RequestPath("/download/{id}")
public HttpResponse download(HttpRequest request, String id) {
log.info("id:{}", id);
File file = fileService.getUploadFile(id);
int available;
try {
@Cleanup
InputStream inputStream = new FileInputStream(file);
available = inputStream.available();
byte[] fileBytes = new byte[available];
inputStream.read(fileBytes, 0, available);
String contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8";
HttpResponse response = Resps.bytesWithContentType(request, fileBytes, contentType);
return response;
} catch (IOException e) {
e.printStackTrace();
return Resps.json(request, RespBodyVo.fail("Error generating captcha"));
}
}
}
上传文件 请求示例
curl --location --request POST 'http://localhost/file/upload' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--form 'file=@"test-chinese.docx"'
响应示例
{
"data": {
"file": null,
"docId": "6632a4ba-ae03-4f05-adce-de6acdd5c560",
"saveFolder": "upload",
"suffixName": ".docx",
"filename": "test-chinese.docx"
},
"ok": true,
"code": null,
"msg": null
}
- 下载文件 http://localhost/file/download/6632a4ba-ae03-4f05-adce-de6acdd5c560
RequestHanlder 版本
tio-boot 的 controller 因为使用了反射,性能会低一些.如果想要提高性能,推荐使用 tio-boot request handler,使用方式如下
添加 Handler
package com.litongjava.file.server.requesthandler;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import com.litongjava.file.server.model.FileSaveResult;
import com.litongjava.file.server.services.FileService;
import com.litongjava.jfinal.aop.annotation.AAutowired;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.resp.RespBodyVo;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FileRequestHanlder {
@AAutowired
private FileService fileService;
public HttpResponse upload(HttpRequest request) {
UploadFile file = request.getUploadFile("file");
RespBodyVo respVo;
if (file != null) {
String name = file.getName();
byte[] fileData = file.getData();
log.info("upload file size:{}", fileData.length);
// save file
FileSaveResult fileSaveResult = fileService.save(name, fileData);
fileSaveResult.setFile(null);
log.info("save file finished");
respVo = RespBodyVo.ok(fileSaveResult);
} else {
respVo = RespBodyVo.fail("fail").code(-1);
}
return Resps.json(request, respVo);
}
public HttpResponse download(HttpRequest request) {
String id = request.getParam("id");
log.info("id:{}", id);
File file = fileService.getUploadFile(id);
int available;
try {
@Cleanup
InputStream inputStream = new FileInputStream(file);
available = inputStream.available();
byte[] fileBytes = new byte[available];
inputStream.read(fileBytes, 0, available);
// String contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8";
String contentType = "";
HttpResponse response = Resps.bytesWithContentType(request, fileBytes, contentType);
return response;
} catch (IOException e) {
e.printStackTrace();
return Resps.json(request, RespBodyVo.fail("Error generating captcha"));
}
}
}
使用配置类配置 RequestHanlder
package com.litongjava.file.server.config;
import com.litongjava.file.server.requesthandler.FileRequestHanlder;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.jfinal.aop.annotation.BeforeStartConfiguration;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.http.server.handler.SimpleHttpRoutes;
@BeforeStartConfiguration
public class HttpServerRequestHanlderConfig {
@Initialization
public void httpRoutes() {
// 创建simpleHttpRoutes
SimpleHttpRoutes simpleHttpRoutes = new SimpleHttpRoutes();
// 创建controller
FileRequestHanlder asrSubmitRequestHanlder = Aop.get(FileRequestHanlder.class);
// 添加action
simpleHttpRoutes.add("/upload", asrSubmitRequestHanlder::upload);
simpleHttpRoutes.add("/download", asrSubmitRequestHanlder::download);
// 将simpleHttpRoutes添加到TioBootServer
TioBootServer.me().setHttpRoutes(simpleHttpRoutes);
}
}
- 开源地址 https://github.com/litongjava/tio-boot-file-server