设为首页 加入收藏

TOP

Eclipse+Spring MVC+HDFS文件云存储源码解析
2019-04-18 00:16:02 】 浏览:69
Tags:Eclipse Spring MVC HDFS 文件 存储 源码 解析
版权声明:原创文章,转载请注明出处! https://blog.csdn.net/L_15156024189/article/details/82015864

一、文件存储策略

1、同步存储

基本步骤:

(1)上传文件;

(2)检查文件(文件大小和类型等),对符合要求的文件存储到本地文件系统(LFS);

(3)存储LFS成功的文件同步到分布式文件系统(HDFS);

(4)存储HDFS成功的文件,在LFS中创建新的空文件作为上传成功标记;

(5)页面展示LFS存储成功的文件列表;

以上步骤是按顺序执行的,有些不足,例如:第(3)步文件上传至HDFS可能需要等待几秒时间,特别当集群处于关闭时,可能需要等待更长的时间,对用户来说,体验非常不好。

注:

(1)HDFS适合存储大型文件,最好接近128M的整数倍;

(2)本文以存储图片为例进行说明,但是对于图片存储,HBase是一个比较好的选择(下次有时间和大家分享Idea+Spring Boot|Spring Cloud+HBase实现海量图片存储),基本思路:表中创建两个列簇,其中一个存储图片内容,另一个存储图片的基本信息,比如文件大小,存储路径等。

2、异步存储

因为页面显示的是LFS存储成功的文件列表,所以不需要等待文件上传到HDFS后再将数据返回给用户页面,应在文件上传到LFS后即刻返回,服务器同时异步上传文件到HDFS。基本步骤:

(1)上传文件;

(2)检查文件(文件大小和类型等),符合要求的文件存储到本地文件系统(LFS),LFS存储成功的文件异步存储到分布式文件系统(HDFS),并在LFS中创建新的空文件作为上传成功标记;

(3)页面展示LFS存储成功的文件的列表;

二、开发环境和集群

1、Eclipse

本案例使用Eclipse纯手工制作。

2、Spring MVC

Spring MVC是一个非常优秀的Web开发框架,内部提供了文件处理机制。为了尽可能多的体现Spring MVC的优秀之处,使用了

(1)Spring MVC内置的CommonsMultipartResolver对文件大小进行检查,

(2)Spring MVC异常处理机制,

(3)拦截器Interceptor对文件类型进行检查。

3、HDFS集群

Hadoop版本2.7.3,具体集群规划如下:

主机名 IP地址 节点名称
bigdata112 192.168.189.112 master(NameNode / SecondaryNameNode)
bigdata113 192.168.189.113 slave1(DataNode)
bigdata114 192.168.189.114 slave2(DataNode)

三、源码解析

1、视图层(View)

视图层主要负责用户数据发送和返回数据显示。index.jsp源码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-Type" content="text/html; charset=UTF-8">
<meta name="keywords" content="微云,大数据,存储">
<meta name="description" content="大数据智能存储系统,打造一款分布式存储智能系统">
<title>大数据智能存储系统</title>

<style type="text/css">
	* {
		margin: 0;
		padding: 0;
	}
	
	body {
		font-size: 10px;
		font-family: "微软雅黑";
		color: #666;
	}
	
	/*top start*/
	.top {
		width: 100%;
		height: 50px;
		background: #f5f8fa;
		box-shadow: 0px 1px 20px -1px #000;
	}
	
	.top .t-header {
		width: 90%;
		height: 50px;
		margin: 0 auto;
	}
	
	.top .t-header .t-logo {
		float: left;
		margin-top: 10px;
	}
	
	.top .t-header .t-desc {
		float: right;
		line-height: 50px;
		font-size: 14px;
	}
	
	/*top end*/
	
	/*tools start*/
	.tools {
		width: 90%;
		height: 40px;
		margin: 30px auto;
	}
	
	.tools .file {
		width: 90px;
		height: 30px;
		display: block;
		background: #6CF;
		text-align: center;
		line-height: 20px;
		text-decoration: none;
		font-size: 10px;
		color: #fff;
		border-radius: 30px;
		outline: none;
		border: none;
		cursor: pointer;
	}
	
	.tools .file:hover {
		background: #0099cc;
	}
	
	/*tools end*/
	
	/*files start*/
	.files {
		width: 90%;
		height: 100px;
		margin: 0px auto;
	}
	
	.files .f-table {
		width: 100%;
		border-collapse: collapse;
	}
	
	.files .f-table tr td {
		height: 40px;
		border-bottom: 1px solid #e5e5e5;
		text-align: center;
		font-size: 12px;
	}
	
	.files .f-table thead tr td {
		font-size: 12px;
		font-weight: bold;
		background: #EEE;
	}
	
	/*files end*/
</style>
</head>
<body>
	<!-- top start -->
	<div class="top">
		<div class="t-header">
			<div class="t-logo">
				<a href="#"> <img src="images/logo.svg" alt="腾讯微云" width="87"
					height="30" />
				</a>
			</div>
			<div class="t-desc">大数据智能存储系统</div>
		</div>
	</div>
	<!-- top end -->

	<!-- tools start -->
	<div class="tools">
		<form id="uploadForm">
			<input type="file" multiple="multiple" id="upload_file"
				style="display: none;" onchange="doUpload();">
			<button type="button" class="file" onclick="upload_file.click();">
				上传文件</button>
		</form>
	</div>
	<!-- tools end -->

	<!-- files start -->
	<div class="files">
		<table class="f-table">
			<thead>
				<tr>
					<td>预览图</td>
					<td>原文件名</td>
					<td>新文件名</td>
					<td>上传时间</td>
					<td>操作</td>
				</tr>
			</thead>
			<tbody id="f-tbody">

			</tbody>
		</table>
	</div>
	<!-- files end -->

<script type="text/java script" src="js/jquery-1.11.1.min.js"></script>
<script type="text/java script" src="js/upload.js"></script>
</body>
</html>

打开页面,效果如下:

2、控制层

控制层主要负责处理用户请求。FileController代码如下:

package com.leboop.controller;

import java.io.File;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.leboop.exception.FileSizeException;
import com.leboop.service.DataService;
import com.leboop.service.FileService;
import com.leboop.service.HDFSService;
import com.leboop.service.LFSService;


@RestController
@RequestMapping("/file")
public class FileController extends FileSizeException{

	private static final long serialVersionUID = 1L;
	@Resource(name="fileService")
	private FileService fileService;
	@Resource(name="hdfsService")
	private HDFSService hdfsService;	
	@Resource(name="lfsService")
	private LFSService lfsService;
	@Resource(name="dataService")
	private DataService dataService;
	
	@RequestMapping("/upload.do")
	@ResponseBody
	public List<Map<String,Object>> upload(
			@RequestParam(value="files")MultipartFile[] mFiles,
				HttpServletRequest request){
		System.out.println("进入controller");
		//上传文件到本地
		String realPath= request.getServletContext().getRealPath("/");
		System.out.println(realPath);
		String storeDir="weiyun/";
		String lfsPath=realPath+storeDir;
		String hdfsPath="/weiyun/";

		List<File> saveToLFSFiles=lfsService.uploadToLocal(mFiles,lfsPath);		
		//上传文件到HDFS	
		List<File> saveToHDFSFiles=hdfsService.uploadToHDFS(hdfsPath,saveToLFSFiles);
		//标记已经同步到HDFS的本地文件
		List<File> newCreatedFiles = lfsService.createNewFile(saveToHDFSFiles);
		
        //异步上传
//		List<File> saveToLFSFiles=fileService.asynUpload(mFiles, lfsPath, hdfsPath);
	
		//页面展示数据
		List<Map<String,Object>> data =dataService.data(storeDir, saveToLFSFiles);
		
		return data;
	}

}

FileController控制器继承自文件大小异常类FileSizeException,代码如下:

package com.leboop.exception;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import com.leboop.service.DataService;

@Controller
public class FileSizeException implements Serializable{
	
	@Resource(name="dataService")
	private DataService dataService;
	private static final long serialVersionUID = 1L;

	@ExceptionHandler(MaxUploadSizeExceededException.class)
	@ResponseBody
	public List<Map<String,Object>> exception(HttpServletRequest request,Exception e){
		List<Map<String,Object>> list = dataService.data(false, "文件过大");
		
		return list;
	}
}

FileSizeException中只有一行代码,但是作用非常大,它处理CommonsMultipartResolver检查文件大小时产生的异常MaxUploadSizeExceededException,并友好提醒用户。事实上Spring MVCy异常处理机制,主要有3种方式:

a、使用Spring MVC提供的SimpleMappingExceptionResolver简单异常处理器

只需要在Spring的xml配置文件中如下配置即可:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property>
        <props>
            <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error</prop>
        </props>
    </property>
</bean>

出现MaxUploadSizeExceededException异常时跳转到error页面。

b、实现HandlerExceptionResolver接口自定义异常处理器

首先实现HandlerExceptionResolver接口,如下:

public class FileSizeException implements HandlerExceptionResolver{
    public ModelAndView resolveException(HttpServletRequest req,HttpServletResponse res,Object handler,Exception e){
        //中间过程处理
        Map<String,Object> map = new HashMap<>();
        map.put("e",e);

        return new ModelAndView("error",map);
    }
}

在Spring xml配置文件中如下配置:

<bean id="exceptionHandler" class="com.leboop.exception.FileSizeException"/>

该方式适合全局处理“有过程的”异常。

前两种方式中返回的是一个跳转页面,对于页面发来的ajax请求,希望局部刷新页面,显示返回数据,所以本案例中使用下面这种方式:

c、使用@HandlerException注解实现异常处理

首先自定义一个异常类FileSizeException,然后使用@HandlerException注解FileSizeException中处理异常的方法,最后让抛出异常的类继承它。

当用户上传文件时,页面会发送请求/file/upload.do,Spring MVC的DispatcherServlet会处理请求路径/file/upload.do,然后HandlerMapping将该请求路径分发到FileController的upload方法中执行。upload调用了服务层的四个方法完成文件上传和页面显示数据处理:

(1)文件上传至LFS

//文件上传到LFS
List<File> saveToLFSFiles=lfsService.uploadToLocal(mFiles,lfsPath);		

(2)文件上传至HDFS,并创建文件上传成功标记

//上传文件到HDFS	
List<File> saveToHDFSFiles=hdfsService.uploadToHDFS(hdfsPath,saveToLFSFiles);
//标记已经同步到HDFS的本地文件
List<File> newCreatedFiles = lfsService.createNewFile(saveToHDFSFiles);
		
//异步上传
//List<File> saveToLFSFiles=fileService.asynUpload(mFiles, lfsPath, hdfsPath);

上面注释的代码是文件异步上传机制(示例中使用的是同步上传),如果使用异步上传,注释掉前两行代码。

(3)页面显示数据处理

//页面展示数据处理
List<Map<String,Object>> data =dataService.data(storeDir, saveToLFSFiles);

3、服务层(Service)

服务层提供了四个服务

(1)Data服务

数据服务主要负责页面上传和回传数据的处理。示例中页面显示的是文件列表,所以数据层需要处理好这些数据并返回给页面。DataService源码如下:

package com.leboop.service;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

@Component("dataService")
public class DataService {
	
	public List<Map<String,Object>> data(boolean flag,String msg){
		List<Map<String,Object>> list = new ArrayList<>();
		Map<String,Object> map = new HashMap<>();
		map.put("uploaded", String.valueOf(flag));
		map.put("msg", msg);
		list.add(map);
		
		return list;
	}
	
	public List<Map<String,Object>> data(String path,List<File> files){
		List<Map<String,Object>> dataList = new ArrayList<>();
		if(files!=null&&files.size()>0){
			for(File f:files){
				Map<String,Object> map= new HashMap<>();
				String newName=f.getName();
				int i=newName.lastIndexOf(".");
				String oldName=newName.substring(0,i);
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				Date date=new Date(Long.parseLong(newName.substring(i+1)));
				
				map.put("uploaded", "true");
				map.put("msg", "成功上传");
				map.put("url", path+newName);
				map.put("oldName", oldName);
				map.put("newName", newName);
				map.put("date", sdf.format(date));
				dataList.add(map);
			}
		}
		
		return dataList;
	}
}

(2)LFS服务

LFS服务主要提供LFS本地文件系统文件上传和下载等服务,示例中需要提供两个功能:文件上传LFS和在LFS创建新的文件标记。LFSService源码如下:

package com.leboop.service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.leboop.entity.LFS;

@Service("lfsService")
public class LFSService {
	@Resource(name="lfs")
	private LFS lfs;
	
	/**
	 * 保存文件到本地文件系统
	 * @param files 要保存的文件列表
	 * @param path 文件保存路径
	 * @return Map<MultipartFile,File> key:保存成功的MultipartFile,保存后的File
	 */
	public List<File> uploadToLocal(MultipartFile[] mFiles,String path){
		List<File> saveFileList=new ArrayList<>();
		
		if(mFiles!=null&&mFiles.length>0){
			 for(MultipartFile mFile:mFiles){
				File f=lfs.uploadToLocal(mFile, path);
				if(f!=null){
					saveFileList.add(f);
				}
			 }
		 }
		
		return saveFileList;
	}

	/**
	 * 修改文件名
	 * @param files 要修改文件名的文件列表
	 * @return 修改文件名后的文件列表
	 */
	public List<File> createNewFile(List<File> files){
		List<File> nFiles = new ArrayList<>();
		if(files!=null&&files.size()>0){
			for(File file:files){
				File f = lfs.createNewFile(file);
				if(f!=null){
					nFiles.add(f);
				}
			}
		}
		
		return nFiles;
	}
}

(3)HDFS服务

HDFS服务和LFS服务类似,它主要提供HDFS分布式文件系统文件的上传和下载等服务。LFSService源码如下:

package com.leboop.service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.leboop.entity.HDFS;

@Service("hdfsService")
public class HDFSService {
	@Resource(name="hdfs")
	private HDFS hdfs;

	/**
	 * 文件上传到HDFS
	 * @param path 文件保存的路径
	 * @param files 要保存的文件列表
	 * @return 成功保存的文件列表
	 */
	public List<File> uploadToHDFS(String path,List<File> files){
		List<File> fileList = new ArrayList<>();
        //设置文件上传缓存大小
	    int buffSize=1024;
	    for(File file:files){
			File f=hdfs.uploadToHDFS(path, file, buffSize);
			if(f!=null){
				fileList.add(f);
			}
	    }

		return fileList;
	}
	
}

(4)File服务

File服务是HDFS和LFS的综合服务。例如示例中,该服务提供文件异步上传服务。FileService源码如下:

package com.leboop.service;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.leboop.entity.HDFS;
import com.leboop.entity.LFS;

@Service("fileService")
public class FileService {
	@Resource(name="lfs")
	private LFS lfs;
	@Resource(name="hdfs")
	private HDFS hdfs;

	private Queue<File> queue = new LinkedList<>();
	private List<File> saveHDFSFileList=new ArrayList<>();
	private List<File> saveLFSFileList=new ArrayList<>();
	private List<File> createNewFileList=new ArrayList<>();
	private boolean flag=false;
	
	public List<File> asynUpload(MultipartFile[] mFiles,String lfsPath,String hdfsPath){
		if(mFiles!=null&&mFiles.length>0){
			int buffSize=1024;
			Thread t = new Thread(
					new Runnable() {
						@Override
						public void run() {
							System.out.println("线程执行开始");
							while(flag==false||!queue.isEmpty()){
								try {
									Thread.sleep(1000);
								} catch (InterruptedException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
								while(!queue.isEmpty()){
									File file =queue.poll();
									File f=hdfs.uploadToHDFS(hdfsPath, file, buffSize);
									if(f!=null){
										saveHDFSFileList.add(f);
										File nf = lfs.createNewFile(file);
										if(nf!=null){
											createNewFileList.add(nf);
										}
									}
								}
							}
							System.out.println("线程执行结束");
						}
					}
			);
			t.start();
			//上传到LFS
			for(MultipartFile mFile:mFiles){
				File file =lfs.uploadToLocal(mFile, lfsPath);
				if(file!=null){
					saveLFSFileList.add(file);
					queue.offer(file);
				}
			}
			flag=true;
		}
		
		return saveLFSFileList;
	}
}

4、实体层

(1)HDFS实体

HDFS实体主要用于连接HDFS,并上传单个文件到HDFS,其实看起来更像一个工具类,HDFS源码如下:

package com.leboop.entity;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("hdfs")
public class HDFS {

	@Value("#{conf.owner}")
	private String owner;
	@Value("#{conf.fs_defaultFS}")
	private String fs_defaultFS;
	
	private FileSystem client;
	private Configuration conf;
	
	@PostConstruct
	/**
	 * 初始化HDFS
	 */
	public void init(){
		System.setProperty("HADOOP_USER_NAME", owner);
		conf=new Configuration();
		conf.set("fs.defaultFS", fs_defaultFS);
		try {
			client= FileSystem.get(conf);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@PreDestroy
	public void destroy(){
		try {
			client.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 上传文件到HDFS
	 * @param path 文件保存路径
	 * @param file 要保存的文件
	 * @param buffSize 缓存大小
	 * @return
	 */
	public File uploadToHDFS(String path, File file,int buffSize){
    	if(file!=null){
    		//输入流
    		InputStream in = null;
            OutputStream out=null;
			//上传文件到HDFS
			try {
				in = new FileInputStream(file);
    	        out=client.create(new Path(path+file.getName()));
            	IOUtils.copyBytes(in, out, buffSize);
            	
            	return file;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				close(in, out);
			}
    	}

		return null;
	}

	/**
	 * 关闭输入和输出流
	 * @param in 输入流
	 * @param out 输出流
	 */
	public void close(InputStream in,OutputStream out){
		try {
			if(in!=null){
				in.close();
			}
			if(out!=null){
				out.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public String getOwner() {
		return owner;
	}
	
	public void setOwner(String owner) {
		this.owner = owner;
	}
	
	public String getFs_defaultFS() {
		return fs_defaultFS;
	}
	
	
	public void setFs_defaultFS(String fs_defaultFS) {
		this.fs_defaultFS = fs_defaultFS;
	}

}

(2)LFS实体

LFS实体主要负责LFS单个文件上传和下载,LFS源码如下:

package com.leboop.entity;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component("lfs")
public class LFS {

	/**
	 * 上传单个文件到本地文件系统
	 * @param mFile 要上传的文件
	 * @param path 上传的路径
	 * @return 返回成功上传的文件,失败返回null
	 */
	public File uploadToLocal(MultipartFile mFile,String path){
		if(mFile!=null){
			//保存文件名
			Date saveDate = new Date();
			String saveFileName=mFile.getOriginalFilename()+"."+saveDate.getTime();
			File saveFile = new File(path+saveFileName);
			//保存文件
			try {
				mFile.transferTo(saveFile);
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			return saveFile;
		}
		
		return null;
	}
	
	public File createNewFile(File file){
		if(file!=null){
			File f=new File(file.getPath()+".COMPLETED");
			try {
				if(f.createNewFile()){
					return f;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}

}

(3)FileChecker

FileChecker主要负责文件类型检查,这里只是为了使用Spring MVC拦截器机制。

package com.leboop.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("fileChecker")
public class FileChecker {
	@Value("#{conf.maxFileSize}")
	private Long maxFileSize;
	@Value("#{conf.fileSuffix}")
	private String fileSuffix;
	
	/**
     * 判断是否为允许的上传文件类型,true表示允许
     */
    public boolean checkFileType(String fileName) {
        //设置允许上传文件类型
        String suffixList = this.getFileSuffix();
        // 获取文件后缀
        int i=fileName.lastIndexOf(".");
        if(i>-1){
        	String suffix = fileName.substring(i+ 1, fileName.length());
        	if(suffix!=null&&suffix.length()>0){
        		if (suffixList.contains(suffix.trim().toLowerCase())) {
                    return true;
                }
        	}
        }

        return false;
    }
	
	public boolean CheckFileSize(long size){
		if(size>maxFileSize){
			return false;
		}
		
		return true;
	}
	
	public Long getMaxFileSize() {
		return maxFileSize;
	}
	public void setMaxFileSize(Long maxFileSize) {
		this.maxFileSize = maxFileSize;
	}
	public String getFileSuffix() {
		return fileSuffix;
	}
	public void setFileSuffix(String fileSuffix) {
		this.fileSuffix = fileSuffix;
	}
	
}

5、拦截器(Interceptor)

拦截器是Spring MVC提供了面向切面编程的技术,文件类型检查拦截器FileTypeInterceptor源码如下:

package com.leboop.interceptor;


import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.apache.struts2.json.JSONUtil;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.leboop.entity.FileChecker;
import com.leboop.service.DataService;

/**
 * 文件类型拦截器
 * @author leboop
 *
 */
public class FileTypeInterceptor implements HandlerInterceptor{

	@Resource(name="fileChecker")
	private FileChecker fChecker;
	@Resource(name="dataService")
	private DataService DataService;
	
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
		// TODO Auto-generated method stub
		response.setCharacterEncoding("utf-8");
		
        // 判断是否为文件上传请求
        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
            Map<String, List<MultipartFile>> files = mRequest.getMultiFileMap();
            Iterator<String> keyIt = files.keySet().iterator();
            //遍历文件
            while (keyIt.hasNext()) {
                String key = (String) keyIt.next();
                Iterator<MultipartFile> valueIt= files.get(key).iterator();
                while(valueIt.hasNext()){
                	MultipartFile mFile=	(MultipartFile)valueIt.next();
                			String ems="";
                	String fn=mFile.getOriginalFilename();
                    //检查文件类型
                    if (!fChecker.checkFileType(fn)) {
                    	ems="文件"+fn+"格式错误!仅支持"+fChecker.getFileSuffix();
                    	List<Map<String,Object>> list = DataService.data(false, ems);
                    	
                    	response.getWriter().print(JSONUtil.serialize(list));

                    	return false;
                    }
//                    //检查文件大小
//                    if(!fChecker.CheckFileSize(mFile.getSize())){
//                    	ems="ERROR-FILE"+"文件"+fn+"超过最大"+fChecker.getMaxFileSize()/1024+"KB";
//                    	PrintWriter out = response.getWriter();  
//                        out.print(ems);
//                    	
//                    	return false;
//                    };
                }
            }
        }
		
		return true;
	}
	
}

四、结果

1、页面

文件上传LFS成功后,页面显示如图:

2、LFS

LFS文件上传成功后如图:

我们对成功后上传到LFS中的文件添加了时间戳,对成功上传到HDFS的文件生成了一个以COMPLETED结尾的标记文件。

3、HDFS

文件上传到HDFS后,如图:

我们看到每个文件分配的数据块大小(Block Size)都是128M,实际大小(Size)只有几十KB。

五、坑(Pit)

下面我们分享一下在实现这个功能中,填过得坑:

pit-1:

ajax给用户的体验比较好,它基于局部刷新页面,比如说,我们示例中只需要刷新文件列表部分,其他部分应当正常显示。我们在页面中使用的是ajax请求,但是对文件大小异常处理的三个方式中,只有第三种方式不用跳转页面直接返回数据。所以选择了第三种方式。但又出现了下面这个坑:

pit-2:

Spring MVC抛出文件大小异常MaxUploadSizeExceededException是在控制层之前,甚至在拦截器之前。FileController继承自FileSizeException,只能处理该控制器中抛出的异常,无法捕获到文件大小异常。所以我们在Spring xml中如下配置了resolveLazily属性:

<property name="resolveLazily" value="true"/>

resolveLazily解析文件时抛出异常。所以让MaxUploadSizeExceededException延迟到了FileController中抛出。

pit-3:

当集群处于关闭状态时,我们继续上传文件,程序会在HDFS连接创建路径时,等待非常长时间,如下:

out=client.create(new Path(path+file.getName()));

处理办法:

(1)修改Hadoop配置文件中的连接超时时间

(2)程序中指定超时时间,代码如下:

final ExecutorService exec = Executors.newFixedThreadPool(1);  
        
Callable<OutputStream> call = new Callable<OutputStream>() {  
    public OutputStream call() throws Exception {  
        OutputStream result=client.create(new Path(path+file.getName()));
            	
        return result;  
    }  
};  
          
try {  
    Future<OutputStream> future = exec.submit(call);  
    OutputStream out = future.get(5, TimeUnit.SECONDS); //任务处理超时时间设为 1 秒  
} catch (TimeoutException ex) {
    ex.printStackTrace();  
} catch (Exception e) {  
    e.printStackTrace();  
}  
// 关闭线程池  
exec.shutdown();

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇数据导入终章:如何将HBase的数据.. 下一篇java操作hdfs文件系统上的文件

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目