总结一个文件上传的案例,使用到的主要技术有:

1. angularJS文件上传,
2. Spring MVC 多媒体解析器组件:CommonsMultipartResolver,
3. FastDFS分布式文件系统。

1.FastDFS分布式文件系统

概述

百度百科:一个开源的轻量级分布式文件系统,FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
用来存储文件的服务器,是用c语言编写的,我们看一下它的系统架构:
FastDFS+MultipartFile+angularJS文件上传
简单介绍一下:FastDFS 服务端包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 是用来管理调度Storage群的 ,Storage会定时向Tracker server发送报告(心跳检查),如果哪个没有发送,Tracker会认为这个Storage已经宕机,不会再调用它。所以实现了高可用性。
Storage也是一个分布式+集群的架构,所以访问时,要通过nginx反向代理访问。

文件上传流程
FastDFS+MultipartFile+angularJS文件上传
客户端上传文件后存储服务器将文件 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…完了把!

相关文章: