今天给大家上道硬菜,坐等有缘人发现,也希望能帮助到有这这需求方面的老哥。
我们在做项目的时候,有一些文件上传的需求,比如个人简历上的大头照,某些调查报告,pdf文档,还有小视屏啥之类的,这些通通都是文件,用户在上传文件的时候,必须用到文件上传功能,那么这个文件就存放到我们服务器对应的文件夹下,在数据库生成一条文件存放地址,这样下次求可以通过域名ip获取到我们存放的文件,废话不多说,我们开始实现。
先来把框架搭起来:
环境要求
Java1.8及以上
本文采用Java 1.8.0_73 . Spring Boot 2.0 + TKmybatis
导入项目maven依赖
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13 </version>
</dependency>
<!--阿里json包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--alibaba easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
<!--springboot起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mysql连接依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.46</version>
</dependency>
<!--spring-boot测试工具起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
项目结构:
application.yml文件配置
server:
port: 8888
servlet:
# 上下文路径
context-path: /api
tomcat:
uri-encoding: UTF-8
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
pageSizeZero: false #pageSize=0
spring:
application:
# 项目名称
name: zigao
profiles:
# 环境
active: dev
jackson:
# json 序列化配置
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT
http:
# 编码
encoding:
charset: UTF-8
enabled: true
force: true
#mvc静态资源访问路径
mvc:
static-path-pattern: /resources/**
resources:
static-locations: file:${file.uploadFileDir}
application-dev.yml文件配置
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
#设置文件大小
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
type-aliases-package: com.zigao
configuration:
call-setters-on-nulls: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
file:
uploadFileDir: D:\zigaoProject
uploadTempDir: D:\zigaoProject
uploadContent: /file
/**
* 启动类
*
* @author zigao
* @date 2020年08月01日 11:46
*/
@SpringBootApplication
@MapperScan("com.zigao.com.dao")
public class zigaoApplication {
public static void main(String[] args) {
SpringApplication.run(zigaoApplication.class, args);
System.out.println("===============================已启动================================");
}
}
注意 :注意 : 注意 这里要特别注意:这里一定要导入tk下的包,一定要导入tk下的包,一定要导入tk下的包,重要的事情说三遍
不然就会报如下错:
还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层
还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层
还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层
启动成功,框架算是搭建好了。
定义数据统一返回格式:
/**
*
*
* @author zigao
* @date 2020年08月01日 11:48
*/
public class ResponseMessage implements Serializable {
int status;
Object message;
String errmsg;
public int getStatus() {
return this.status;
}
public void setStatus(int status) {
this.status = status;
}
public Object getMessage() {
return this.message;
}
public void setMessage(Object message) {
this.message = message;
}
public String getErrmsg() {
return this.errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
public ResponseMessage() {
}
public ResponseMessage(int status) {
this.status = 0;
}
public ResponseMessage(Object data) {
this.status = 0;
this.message = data;
}
public ResponseMessage(int status, Object data) {
this.status = status;
this.message = data;
}
public ResponseMessage(int status, String err) {
this.status = status;
this.errmsg = err;
}
}
我们把表设计一下:这个表主要用来存储文件上传的一些信息比如,文件编码,录入单位,录入人,文件名称,文件类型等等。
我们一个个解释下:
文件编号 : 指定这个上传的文件唯一性,必须要唯一。
文件名称:这个文件叫啥 比如哪个美女的靓照
表名:这个可以理解,这条信息对应哪张业务表
对应表主键: 就是这条信息对应哪张业务表的主键
文件格式:比如这个文件是小电影,那他就是视屏格式,如果是文档 就是文档格式
文件类型:主要根据业务描述类型信息
文件路径:用来存储文件所在服务器的路径信息
文件说明:比如这个小电影看起来很nice
文件时间:这个文件什么时候上传进来的
录入人:就是谁上传的这个文件
录入单位:哪个单位负责录入这个文件的
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for file_infos
-- ----------------------------
DROP TABLE IF EXISTS `file_infos`;
CREATE TABLE `file_infos` (
`FILE_CODE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件编号',
`FILE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件名称',
`TABLE_NAME` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '表名',
`OBJECT_ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '对应表主键',
`FILE_FORMAT` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件格式',
`FILE_TYPE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件类型',
`FILE_URL` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件路径',
`DESCRIPTION` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件说明',
`FILE_TIME` datetime(0) NULL DEFAULT NULL COMMENT '文件时间',
`USER_CODE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '录入人',
`FILLED_UNIT` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '录入单位',
PRIMARY KEY (`FILE_CODE`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文件表' ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
@Data
public class FileInfos implements Serializable {
private static final long serialVersionUID = 8478273528550897842L;
/**
* 文件编号
*/
private String fileCode;
/**
* 文件名称
*/
private String fileName;
/**
* 表名
*/
private String tableName;
/**
* 对应表主键
*/
private String objectId;
/**
* 文件格式
*/
private String fileFormat;
/**
* 文件类型
*/
private String fileType;
/**
* 文件路径
*/
private String fileUrl;
/**
* 文件说明
*/
private String description;
/**
* 文件时间
*/
private Timestamp fileTime;
/**
* 录入人
*/
private String userCode;
/**
* 录入单位
*/
private String filledUnit;
}
文件上传工具类:
public class FileUtils {
/**
* 创建目录,如果目录不存在这创建目录
*
* @param path 目录路径
*/
public static void mkdirs(String path) {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 删除文件
*
* @param path
*/
public static void deleteFile(String path) {
File file = new File(path);
if (file.exists()) {
file.delete();
}
}
/**
* 创建文件
*
* @param stream 文件流
* @param path 文件保存路径
* @throws IOException
*/
public static void createFile(InputStream stream, String path) throws IOException {
FileOutputStream outputStream = null;
int byteCount;
try {
outputStream = new FileOutputStream(path);
byte[] bytes = new byte[1024];
while ((byteCount = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, byteCount);
}
} finally {
closeOutputStream(outputStream);
closeInputStream(stream);
}
}
/**
* 关闭 InputStream 流
*
* @param stream InputStream 流对象
*/
private static void closeInputStream(InputStream stream) {
try {
stream.close();
} catch (IOException e) {
}
}
/**
* 关闭 OutputStream 流
*
* @param outputStream OutputStream 流对象
*/
private static void closeOutputStream(OutputStream outputStream) {
try {
outputStream.close();
} catch (IOException e) {
}
}
/**
* MultipartFile 转 File
*
* @param file
* @throws Exception
*/
public static File multipartFileToFile(MultipartFile file) throws Exception {
File toFile = null;
if (!"".equals(file) && file.getSize() > 0) {
InputStream ins;
ins = file.getInputStream();
toFile = new File(file.getOriginalFilename());
inputStreamToFile(ins, toFile);
ins.close();
}
return toFile;
}
/**
* 获取流文件
*/
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getPath() {
return FileUtils.class.getResource("/").getPath();
}
}
Controller层:
@RestController
@RequestMapping("/file")
public class FileController {
//打印日志
public Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private FileService fileService;
/**
* 上传文件
*
* @param file
* @return
*/
@PostMapping("/upload")
public ResponseMessage upload(@RequestParam("file") MultipartFile file) {
ResponseMessage responseMessage = new ResponseMessage(0);
try {
FileInfos fileInfo = fileService.upload(file);
responseMessage.setMessage(fileInfo);
} catch (Exception e) {
logger.error("上传文件异常,", e);
throw new RuntimeException(e.getMessage());
}
return responseMessage;
}
}
Service层:
public interface FileService {
FileInfos createFile(MultipartFile file);
FileInfos upload(MultipartFile file);
void addFiles(FileInfos fileInfo);
}
@Service
public class FileServiceImpl implements FileService {
@Autowired
private FileInfoMapper fileInfoMapper;
@Value("${file.uploadFileDir}")
private String uploadFileDir;
@Value("${file.uploadContent}")
private String uploadContent;
@Override
public FileInfos createFile(MultipartFile file) {
//获取文件基本信息
String originalName = file.getOriginalFilename();
String suffix = originalName.substring(originalName.lastIndexOf("."));
String name = generateFileName() + suffix;
FileInfos fileInfos = new FileInfos();
try {
//创建文件路径
String monthPath = getMonthPath();
String filePath = monthPath + "/" + name;
String dir = uploadFileDir + uploadContent + "/" + monthPath;
String savePath = uploadContent + "/" + filePath;
FileUtils.mkdirs(dir);
//上传文件
FileUtils.createFile(file.getInputStream(), dir + "\\" + name);
//保存附件
fileInfos.setFileName(originalName);
fileInfos.setFileUrl(savePath);
fileInfos.setFileTime(Timestamp.valueOf(LocalDateTime.now()));
fileInfos.setFileFormat(suffix);
fileInfos.setFileCode(UUID.randomUUID().toString().replaceAll("\\-", ""));
fileInfoMapper.insert(fileInfos);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return fileInfos;
}
@Override
public FileInfos upload(MultipartFile file) {
FileInfos fileInfo = this.createFile(file);
return fileInfo;
}
/**
* 生成文件名称
*
* @return
*/
private String generateFileName() {
Format format = new SimpleDateFormat("yyyyMMddHHmmss");
return format.format(new Date()) + UUID.randomUUID().toString().replace("-", "");
}
/**
* 按照月份存储
*
* @return
*/
private String getMonthPath() {
Format format = new SimpleDateFormat("yyyyMM");
return format.format(new Date());
}
@Override
public void addFiles(FileInfos fileInfo) {
fileInfo.setFileCode(UUID.randomUUID().toString());
fileInfoMapper.insert(fileInfo);
}
}
dao层:
@Repository
public interface FileInfoMapper extends Mapper<FileInfos> {
}
代码写完啦,结构如下:我们来测试下:
上传成功,并返回结果信息。
打开文件目录,在对应文件下,证明上传成功。
OK,下面我们来测试下访问上传的图片。
好,整体功能就写完了,有哪里不明白的老哥,或者哪里写的不好的地方,欢迎指点一二。