前面的文章《Java编程第42讲——使用SpringBoot实现静态网站》中,用SpringBoot实现了一个古诗词网。其实,在这个服务器的static目录中放入其他的文件,譬如readme.txt文件,通过浏览器可以直接查看,通过迅雷,也可以直接下载:
但是,把这个功能当成文件下载功能,就太粗糙了。个人认为,即使最简单的文件下载服务器,也至少具备下面两个要点:
(1)通过浏览器输入要下载的资源的URL后,浏览器可以保存附件;
(2)软件工程师可以根据需要,添加控制文件下载权限的代码。
下面,我们基于SpringBoot,使用IDEA开发工具,开发一个简单的文件下载服务器吧。
第1步:使用IDEA创建一个空项目fc_prj,然后在fc_prj项目中添加一个SpringBoot项目file_center,加入Lombok、Spring Web、Spring Data JPA、MySQL Driver四个依赖。操作完成后项目视图如下:
第2步:在mysql中执行下面的SQL语句,建立本项目使用的数据库db_fc:
create database db_fc default character set utf8;
第3步:在db_fc数据库中建立t_user表,用于进行权限控制,为了让程序显得简单,这里t_user表只包含用户名、密码、备注三个字段,建立t_user表的SQL语句如下:
use db_fc;create table t_user( user_name varchar(32) primary key not null, password varchar(32) not null, comment varchar(64))engine=InnoDB default charset=utf8;
第4步:我们在t_user表中插入张三、李四、王五三个用户的信息,SQL语句如下:
insert into t_user(user_name, password, comment) values('zhangsan', 'zhang', '用户张三');insert into t_user(user_name, password, comment) values('lisa', 'li', '用户李四');insert into t_user(user_name, password, comment) values('wangwu', 'wang', '用户王五');commit;
第5步:在db_fc数据库中建立t_file表,用于记录可以下载的文件信息,该表有文件标识符、文件完整路径两个字段,建立t_file表的SQL语句如下:
use db_fc;create table t_file( file_id varchar(32) primary key not null, file_path varchar(512) not null)engine=InnoDB default charset=utf8;
第6步:我们向t_file表中插入一些可以下载的文件信息,SQL语句如下:
insert into t_file(file_id, file_path) values('flower', '/root/files/flower.jpg');insert into t_file(file_id, file_path) values('hello.txt', '/root/files/hello.txt');commit;
第7步:新建entity包;
第8步:在entity包中建立UserEntity类,一个UserEntity实例对应t_user表的一条记录。UserEntity的代码如下:
package com.flying.file_center.entity;import lombok.Data;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Table;@Data@Table(name="t_user")@Entitypublic class UserEntity { @Id private String userName; private String password; private String comment;}
第9步:在entity包中建立FileEntity类,一个FileEntity实例对应t_file表的一条记录。FileEntity的代码如下:
package com.flying.file_center.entity;import lombok.Data;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Table;@Data@Table(name="t_file")@Entitypublic class FileEntity { @Id private String fileId; private String filePath;}
第10步:新建repository包;
第11步:在repository包中建立UserRepository接口,用于操作t_user表,代码如下:
package com.flying.file_center.repository;import com.flying.file_center.entity.UserEntity;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;import org.springframework.stereotype.Repository;import java.util.List;@Repositorypublic interface UserRepository extends JpaRepository, JpaSpecificationExecutor { List queryUserEntitiesByUserNameAndPassword(String userName, String password);}
第12步:在repository包中建立FileRepository接口,用于操作t_file表,代码如下:
package com.flying.file_center.repository;import com.flying.file_center.entity.FileEntity;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;import org.springframework.stereotype.Repository;import java.util.List;@Repositorypublic interface FileRepository extends JpaRepository, JpaSpecificationExecutor { List queryFileEntitiesByFileId(String fileId);}
第13步:新建controller包和service包:
第14步:在service包中建立FileService类,用于实现处理HTTP下载请求,代码如下:
package com.flying.file_center.service;import com.flying.file_center.entity.FileEntity;import com.flying.file_center.entity.UserEntity;import com.flying.file_center.repository.FileRepository;import com.flying.file_center.repository.UserRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.OutputStream;import java.util.List;@Servicepublic class FileService { @Autowired private FileRepository fileRepository; @Autowired private UserRepository userRepository; public void download(String userName, String password, String fileId, HttpServletResponse httpServletResponse){ List userEntityList = userRepository.queryUserEntitiesByUserNameAndPassword(userName, password); if (userEntityList.size() == 0){ httpServletResponse.setStatus(401); return; } List fileEntityList = fileRepository.queryFileEntitiesByFileId(fileId); if (fileEntityList.size() == 0){ httpServletResponse.setStatus(404); return; } File file = new File(fileEntityList.get(0).getFilePath()); if (file.exists()) { httpServletResponse.setContentType("application/force-download"); httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileEntityList.get(0).getFileId()); byte[] bytesBuffer = new byte[1024]; try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); ) { OutputStream outputStream = httpServletResponse.getOutputStream(); int i = bis.read(bytesBuffer); while (i != -1) { outputStream.write(bytesBuffer, 0, i); i = bis.read(bytesBuffer); } } catch (Exception e) { e.printStackTrace(); } }else{ httpServletResponse.setStatus(404); } }}
第15步:在controller包中建立FileController类,用于接收来自浏览器或其他客户端的HTTP下载请求,代码如下:
package com.flying.file_center.controller;import com.flying.file_center.service.FileService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;@RestController@Slf4jpublic class FileController { @Autowired private HttpServletResponse httpServletResponse; @Autowired private FileService fileService; @GetMapping("/download/{userName}/{password}/{fileId}") public void download(@PathVariable(value="userName") String userName, @PathVariable(value="password") String password, @PathVariable(value="fileId") String fileId){ fileService.download(userName, password, fileId, httpServletResponse); }}
第16步:修改application.properties文件,加入服务器的配置信息,这里只配置服务器使用的端口和数据库连接信息,application.properties文件的内容如下:
server.port=9201spring.datasource.url=jdbc:mysql://127.0.0.1/db_fc?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=truespring.datasource.username=rootspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.max-idle=10spring.datasource.max-wait=10000spring.datasource.min-idle=5spring.datasource.initial-size=5
第17步:修改pom.xml文件,加入编译和打包的信息,修改后的pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>4.0.0org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASEcom.flying file_center 1.0.1file_centerDemo project for Spring Boot1.8org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtimeorg.projectlombok lombok trueorg.springframework.boot spring-boot-starter-test testorg.junit.vintage junit-vintage-engine fcorg.apache.maven.plugins maven-jar-plugin *.properties*.txt*.xml true fc_lib/falsecom.flying.file_center.FileCenterApplication./resources/${project.build.directory}org.apache.maven.plugins maven-dependency-plugin copy-dependenciespackagecopy-dependencies ${project.build.directory}/fc_lib/ maven-resources-plugin copy-resourcespackagecopy-resourcessrc/main/resources${project.build.directory}/resources
第18步:在IDEA的Terminal窗口输入mvn clean package -DskipTests命令,完成程序的编译与打包,得到fc.jar和相关的文件:
第19步:将fc.jar文件和依赖的库文件和资源文件拷贝到Linux运行环境:
第20步:在Linux的/root目录中新建files目录,然后向该目录中传入两个文件flower.jpg和hello.txt:
第21步:执行java -jar fc.jar命令,启动文件下载服务器:
第22步:当我们在浏览器中输入http://:9201/download/zhangsan/zhang/flower时,浏览器会自动下载文件flower.jpg,并且生成的文件名是我们指定的FileId,也就是flower:
第23步:当我们在浏览器中故意输错用户名或密码,例如输入http://:9201/download/zhangsan/zhang2/flower时,将会得到HTTP 401错误:
第23步:当我们在浏览器中故意输错文件ID,例如输入http://:9201/download/zhangsan/zhang/flower.jpg时,将会得到HTTP 404错误:
开发完成!