总结一个文件上传的案例,使用到的主要技术有:
1. angularJS文件上传,
2. Spring MVC 多媒体解析器组件:CommonsMultipartResolver,
3. FastDFS分布式文件系统。
1.FastDFS分布式文件系统
概述
百度百科:一个开源的轻量级分布式文件系统,FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
用来存储文件的服务器,是用c语言编写的,我们看一下它的系统架构:
简单介绍一下:FastDFS 服务端包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 是用来管理调度Storage群的 ,Storage会定时向Tracker server发送报告(心跳检查),如果哪个没有发送,Tracker会认为这个Storage已经宕机,不会再调用它。所以实现了高可用性。
Storage也是一个分布式+集群的架构,所以访问时,要通过nginx反向代理访问。
文件上传流程
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。
java客户端使用:
FastDFS 的安装一般是由运维工程师完成的,我们开发人员需要知道其ip地址。
写一个上传文件的demo:
1.导入依赖
<dependency>
<groupId>org.csource.fastdfs</groupId>
<artifactId>fastdfs</artifactId>
<version>1.2</version>
</dependency>
这个jar中央仓库没有,我们要自己上传到本地仓库
2.配置文件:
connect_timeout = 2
network_timeout = 30
charset = ISO8859-1
http.tracker_http_port = 8080
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
tracker_server = IP地址:22122
将我们的FastDFS服务器ip地址填写进去,这个配置文件哪来的,安装好FastDFS后,它自带有一些启动脚本及配置文件,这个配置文件的目录是/etc/fdfs/client.conf(把注释去掉就找这个样子)。
3.代码
public class FastDFSDemo {
public static void main(String[] args) throws Exception {
//加载配置文件
ClientGlobal.init("D:\\IdeaProjects\\fastdfs-demo2\\src\\fdfs_client.conf");
//创建TrackerClient,调用方法创建TrackerServer
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
//创建StorageClient,第二个参数是StorageServer我们这里有全局变量初始化ClientGlobal.init,会帮我们去找服务器?(自己想的)
StorageClient storageClient = new StorageClient(trackerServer, null);
//调用方法上传文件
String[] strings = storageClient.upload_file("E:\\picture\\lin.jpg", "jpg", null);
//查看返回的fileID,两个元素,第一个是组名,第二个是文件名
for (String string : strings) {
System.out.println(string);
}
}
}
4.控制台:
group1
M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg
fileId包括:组名,虚拟磁盘路径,数据两级目录,文件名。创建StorageClient时我们可以创建fastdfs提供的另一个api: StorageClient1,其有一个上传方法是
String upload_file1(TrackerServer trackerServer, StorageServer storageServer),
可以直接返回组名+文件的全FileId。
2. Spring MVC 文件上传
前端:
文件上传表单要求满足三点要求:
1.表单的enctype属性的值设置为multipart/form-data
2.表单要有type=file文件上传输入框
3.表单的请求方式必须是post
<form action="file/springmvcFileUpload" method="post" enctype="multipart/form-data">
<input type="file" name="upload"/>
<input type="submit" value="上传">
</form>
后端:
在springmvc配置文件中配置多媒体文件解析器:CommonsMultipartResolver(Spring MVC 提供的解析文件的组件),它可以自动解析multipart格式的数据,multipart是一个用随机字符串分割数据内容的数据格式(大概就这样,下面说angularJS有),所以解析器可以获得文件名,格式,二进制数据等内容。
<!--id必须为multipartResolver,且必须写-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值 5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
在对应的上传方法声明上定义参数MultipartFile file,springmvc会帮我们把上传的文件的封装到者file对象中:
@RestController
public class UploadController {
@Value("${FILE_SERVER_URL}")
private String FILE_SERVER_URL;
@RequestMapping("/upload")
public Result upload(MultipartFile file){
try {
//获取文件扩展名
String name = file.getOriginalFilename();
int index = name.lastIndexOf(".");
String extName = name.substring(index + 1);
//上传文件,配置文件就是上面说的那个
FastDFSClient fastDFSClient = new FastDFSClient("classpath:config/fdfs_client.conf");
String path = fastDFSClient.uploadFile(file.getBytes(), extName);
//拼接资源路径
String url = FILE_SERVER_URL + path;
return new Result(true,url);
}catch (Exception e){
e.printStackTrace();
return new Result(false, "上传失败");
}
}
}
FastDFSClient工具类:(可以略过,就是上面FastDFS的java客户端上传文件代码的封装)
public class FastDFSClient {
private TrackerClient trackerClient = null;
private TrackerServer trackerServer = null;
private StorageServer storageServer = null;
private StorageClient1 storageClient = null;
public FastDFSClient(String conf) throws Exception {
if (conf.contains("classpath:")) {
conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
System.out.println(conf);
}
ClientGlobal.init(conf);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageServer = null;
storageClient = new StorageClient1(trackerServer, storageServer);
}
/**
* 上传文件方法
* <p>Title: uploadFile</p>
* <p>Description: </p>
* @param fileName 文件全路径
* @param extName 文件扩展名,不包含(.)
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileName, extName, metas);
return result;
}
public String uploadFile(String fileName) throws Exception {
return uploadFile(fileName, null, null);
}
public String uploadFile(String fileName, String extName) throws Exception {
return uploadFile(fileName, extName, null);
}
/**
* 上传文件方法
* <p>Title: uploadFile</p>
* <p>Description: </p>
* @param fileContent 文件的内容,字节数组
* @param extName 文件扩展名
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileContent, extName, metas);
return result;
}
public String uploadFile(byte[] fileContent) throws Exception {
return uploadFile(fileContent, null, null);
}
public String uploadFile(byte[] fileContent, String extName) throws Exception {
return uploadFile(fileContent, extName, null);
}
}
3.angularJS文件上传
angularJS是面向变量的JS框架,模块,MVC,依赖注入,双向绑定四大特性,尤其是双向绑定的思想,很牛逼,感叹一下计算机语言前辈,大牛思想的伟大,堪比人类语言好嘛,折射事件万物。
有时间写一下angularJS框架的笔记。今天先说文件上传:
定义文件上传服务
app.service("uploadService",function ($http) {
this.uploadFile=function () {
//FormData是html5提供给我们的能够承载二进制数据的对象
var formData = new FormData();
//file.files[0]代表获取name或id为file的输入框中的第一个输入框中内容
formData.append("file",file.files[0]);
//文件上传必须用这种原始请求方式,因为要设置headers
return $http({
method:"post",
url:"../upload.do",
data:formData,
//这两属性下面说
headers:{"Content-Type":undefined},
transformRequest: angular.identity
})
}
});
anjularjs对于post和get请求默认的Content-Type header 是application/json。通过设置‘Content-Type’: undefined,这样浏览器会帮我们把Content-Type 设置为 multipart/form-data.
通过设置 transformRequest: angular.identity ,anjularjs transformRequest function 将序列化我们的formdata object
在controller中引入文件上传服务,并定义函数:
$scope.uploadFile=function(){
uploadService.uploadFile().success(function(response) {
if(response.success){//如果上传成功,取出url
$scope.image_entity.url=response.message;//设置文件地址
}else{
alert(response.message);
}
}).error(function() {
alert("上传发生错误");
});
};
ok,看一下上传的数据:
-----------------------------41184676334
Content-Disposition: form-data; name="file"; filename="ba017682d056f983b8f1656fcbfd5af5.jpg"
Content-Type: image/jpeg
›>ȉl”À\FþðM~PŽ=¦ö»š^Pó(sŽÓéIý♸›{ÝFºÝÁ3²¤{ÜXiÂáû¿@ÂÖÖž¾ÝOÙS—"Àdclc§ÏÉO†BÐC,8Š'¹ÿ
...二进制数据
emm…完了把!