FastDFS的Java客户端

发表时间:2017-07-18 21:41:34 浏览量( 13 ) 留言数( 0 )

学习目标:

1、掌握fastdfs-client的环境搭建。

2、掌握fastdfs-client的基本api的使用


学习过程:

下载客户端源码

https://github.com/happyfish100/fastdfs-client-java

你可以下载源代码后编译成为一个jar包。也可以把整个源代码包拷贝到你的项目中,源代码并不多。

下载编译,

git clone  https://github.com/happyfish100/fastdfs-client-java.git

使用maven从源码安装

mvn clean install

在您的maven项目pom.xml中添加依赖

<dependency>

    <groupId>org.csource</groupId>

    <artifactId>fastdfs-client-java</artifactId>

    <version>1.27-SNAPSHOT</version>

</dependency>


新建一个项目在src/main/resources下面建立一个配置文件,配置文件名fdfs_client.conf

connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8888
http.anti_steal_token = no
http.secret_key = FastDFS1234567890

tracker_server = 192.168.3.101:22122
#tracker_server = 192.168.3.104:22122

注1:tracker_server指向您自己IP地址和端口,1-n个

注2:除了tracker_server,其它配置项都是可选的


封装一个FastDFS的工具类

public class FastDFSClient {

	// private static final String CONF_FILENAME =
	// Thread.currentThread().getContextClassLoader().getResource("").getPath()
	// + "fdfs_client.conf";

	// 测试
	private static final String CONF_FILENAME = "fdfs_client.conf";
	private static StorageClient1 storageClient1 = null;

	private static Logger logger = Logger.getLogger(FastDFSClient.class);

	/**
	 * 静态加载,加载一次.
	 */
	static {
		try {
			String classPath = new File(FastDFSClient.class.getResource("/").getFile()).getCanonicalPath();
			String fdfsClientConfigFilePath = classPath + File.separator + CONF_FILENAME;
			fdfsClientConfigFilePath = java.net.URLDecoder.decode(fdfsClientConfigFilePath,"utf-8");  
			logger.info("=== 配置文件:" + fdfsClientConfigFilePath);
			ClientGlobal.init(fdfsClientConfigFilePath);
			TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
			TrackerServer trackerServer = trackerClient.getConnection();
			if (trackerServer == null) {
				logger.error("getConnection return null");
			}
			StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
			if (storageServer == null) {
				logger.error("getStoreStorage return null");
			}
			storageClient1 = new StorageClient1(trackerServer, storageServer);
		} catch (Exception e) {
			logger.error(e);
		}
	}

	/**
	 * 
	 * @param file
	 *            文件对象
	 * @param file_ext_name
	 *            文件后缀名,如png , txt 等
	 * @return 返回Null则为失败
	 */
	public static String uploadFile(File file, String file_ext_name) {
		FileInputStream fis = null;
		try {
			NameValuePair[] meta_list = null; // new NameValuePair[0];
			fis = new FileInputStream(file);
			byte[] file_buff = null;
			if (fis != null) {
				int len = fis.available();
				file_buff = new byte[len];
				fis.read(file_buff);
			}

			String fileid = storageClient1.upload_file1(file_buff, file_ext_name, meta_list);
			return fileid;
		} catch (Exception ex) {
			logger.error(ex);
			return null;
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					logger.error(e);
				}
			}
		}
	}

	/**
	 * 
	 * @param file_buff
	 *            file content/buff
	 * @param file_ext_name
	 *            文件后缀名,如png , txt 等
	 * @return 返回Null则为失败
	 */
	public static String uploadFile(byte[] file_buff, String file_ext_name) throws Exception {

		String result = storageClient1.upload_file1(file_buff, file_ext_name, null);

		return result;
	}

	/**
	 * 根据组名和远程文件名来删除一个文件
	 * 
	 * @param groupName
	 *            例如 "group1" 如果不指定该值,默认为group1
	 * @param fileName
	 *            例如"M00/00/00/wKgxgk5HbLvfP86RAAAAChd9X1Y736.jpg"
	 * @return 0为成功,非0为失败,具体为错误代码
	 */
	public static int deleteFile(String groupName, String fileName) {
		try {
			int result = storageClient1.delete_file(groupName == null ? "group1" : groupName, fileName);
			return result;
		} catch (Exception ex) {
			logger.error(ex);
			return 0;
		}
	}

	/**
	 * 根据fileId来删除一个文件(我们现在用的就是这样的方式,上传文件时直接将fileId保存在了数据库中)
	 * 
	 * @param fileId
	 *            file_id源码中的解释file_id(包括组名和文件名);例如
	 *            group1/M00/00/00/ooYBAFM6MpmAHM91AAAEgdpiRC0012.xml
	 * @return 0为成功,非0为失败,具体为错误代码
	 */
	public static int deleteFile(String fileId) {
		try {
			int result = storageClient1.delete_file1(fileId);
			return result;
		} catch (Exception ex) {
			logger.error(ex);
			return 0;
		}
	}

	/**
	 * 修改一个已经存在的文件
	 * 
	 * @param oldFileId
	 *            原来旧文件的fileId, file_id(包括组名和文件名);例如
	 *            group1/M00/00/00/ooYBAFM6MpmAHM91AAAEgdpiRC0012.txt
	 * @param file
	 *            新文件
	 * @param file_ext_name
	 *            文件后缀名,如png , txt 等
	 * @return 返回空则为失败
	 */
	public static String modifyFile(String oldFileId, File file, String file_ext_name) {
		String fileid = null;
		try {
			// 先上传
			fileid = uploadFile(file, file_ext_name);
			if (fileid == null) {
				return null;
			}
			// 再删除
			int delResult = deleteFile(oldFileId);
			if (delResult != 0) {
				return null;
			}
		} catch (Exception ex) {
			logger.error(ex);
			return null;
		}
		return fileid;
	}

	/**
	 * 文件下载
	 * 
	 * @param fileId
	 *            file_id源码中的解释file_id(包括组名和文件名);例如
	 *            group1/M00/00/00/ooYBAFM6MpmAHM91AAAEgdpiRC0012.xml
	 * @return 返回一个流
	 */
	public static InputStream downloadFile(String fileId) {
		try {
			byte[] bytes = storageClient1.download_file1(fileId);
			InputStream inputStream = new ByteArrayInputStream(bytes);
			return inputStream;
		} catch (Exception ex) {
			logger.error(ex);
			return null;
		}
	}

	/**
	 * 文件下载
	 * 
	 * @param fileId
	 *            file_id源码中的解释file_id(包括组名和文件名);例如
	 *            group1/M00/00/00/ooYBAFM6MpmAHM91AAAEgdpiRC0012.xml
	 * @return 返回一个流
	 */
	public static byte[] downloadFileFoBytes(String fileId) {
		try {
			byte[] bytes = storageClient1.download_file1(fileId);
			return bytes;
		} catch (Exception ex) {
			logger.error(ex);
			return null;
		}
	}

	/**
	 * 获取文件后缀名(不带点).
	 * 
	 * @return 如:"jpg" or "".
	 */
	@Deprecated
	public static String getFileExt(String fileName) {
		if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
			return "";
		} else {
			return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
		}
	}
}


测试类:

public class FastDFSClientTest {

	@Test
	public void testuploadFile() {

		File file = new File("d://abc.txt");

		String result = FastDFSClient.uploadFile(file, "txt");
		System.out.println(result);
	}

	@Test
	public void testdeleteFile() {

		int result = FastDFSClient.deleteFile("group1/M00/00/00/wKgA1lmBmEqALVpKAAAABufA-1A5349637");
		System.out.println(result);
	}

	@Test
	public void testdeleteFileGroup() {
		int result = FastDFSClient.deleteFile("group1", "M00/00/00/wKgA1lmBnWKAMVLdAAAABufA-1A229.txt");
		System.out.println(result);
	}

	@Test
	public void testdownloadFile() throws IOException {

		InputStream inputStream = FastDFSClient.downloadFile("group1/M00/00/00/wKgA1loks8CAXQoPAAL9YmvnNBw317.png");

		System.out.println(inputStream);

		byte[] buffer = new byte[1024];
		int bytesRead = 0;

		// 从文件中按字节读取内容,到文件尾部时read方法将返回-1
		while ((bytesRead = inputStream.read(buffer)) != -1) {

			// 将读取的字节转为字符串对象
			String chunk = new String(buffer, 0, bytesRead);
			System.out.print(chunk);
		}

	}

	@Test
	public void testdownloadFileByte() throws IOException {

		byte[] bytes = FastDFSClient.downloadFileFoBytes("group1/M00/00/00/wKgA1loks8CAXQoPAAL9YmvnNBw317.png");
		
		System.out.println(bytes.length);
		
		//String str=new String(bytes, 0, bytes.length);
		//System.out.println(str);

	}
}


运行第一个测试类得到的结果如下:

2018-04-15 22:59:46,843  INFO [FastDFSClient.java:38] : === 配置文件:E:\project\JavaDayUpSource\中级阶段\补充知识点\fastDFSstu\target\test-classes\fdfs_client.conf

group1/M00/00/00/wKgDZ1rTNcuADr4aAAAADA9sp64017.txt


和spring MVC可以结合上传下载等操作。Spring的congtroller:

@Controller
public class FileControl {
	
	

	/**
	 * 
	* @Description: 单文件上传
	* @author: liubao
	* @param 
	* @return 
	 * @throws IOException 
	 * @throws IllegalStateException 
	* @date:  2018年2月7日 下午2:59:05
	 */
	@RequestMapping(value = "/fileUpload", consumes = "multipart/form-data")
	public String fileUpload(@RequestParam("file") MultipartFile file, @RequestParam("description") String description,HttpServletRequest request) throws IllegalStateException, IOException {

		 //如果文件不为空,写入上传路径
        if(!file.isEmpty()) {
            //上传文件路径
            String path = request.getServletContext().getRealPath("/images/");
            //上传文件名
            String filename = file.getOriginalFilename();
            File filepath = new File(path,filename);
            //判断路径是否存在,如果不存在就创建一个
            if (!filepath.getParentFile().exists()) { 
                filepath.getParentFile().mkdirs();
            }
            //将上传文件保存到一个目标文件当中
            file.transferTo(new File(path + File.separator + filename));
            return "success";
        } else {
            return "error";
        }

	}

	/**
	 * 
	* @Description:  多文件上传
	* @author: liubao
	* @param 
	* @return 
	* @date:  2018年2月7日 下午2:58:57
	 */
	@RequestMapping(value = "/fileUploads", consumes = "multipart/form-data")
	public String fileUploads(@RequestParam("files") CommonsMultipartFile files[], HttpServletRequest request,
			ModelMap model) {

		System.out.println(request.getParameter("name"));

		for (MultipartFile file : files) {
			System.out.println(file.getOriginalFilename());
            //文件处理省了....
		}
		return "hello";
	}

	/**
	 * 
	* @Description: 文件下载
	* @author: liubao
	* @param 
	* @return 
	* @date:  2018年2月7日 下午2:59:52
	 */
	@RequestMapping(value = "/fileDown")
	public ResponseEntity<byte[]> download(HttpServletRequest request,
			Model model) throws Exception {
		// 下载文件路径

		byte[] bytes = FastDFSClient.downloadFileFoBytes("group1/M00/00/00/wKgA1loks8CAXQoPAAL9YmvnNBw317.png");
		
		System.out.println(bytes.length);

		HttpHeaders headers = new HttpHeaders();
		// 下载显示的文件名,解决中文名称乱码问题
		String downloadFielName = new String("下载文件名称".getBytes("UTF-8"), "iso-8859-1");
		// 通知浏览器以attachment(下载方式)打开图片
		headers.setContentDispositionFormData("attachment", downloadFielName);
		// application/octet-stream : 二进制流数据(最常见的文件下载)。
		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
		return new ResponseEntity<byte[]>(bytes, headers, HttpStatus.CREATED);
	}

}


这里需要注意一下,就是文件的后缀名,一般不能直接使用文件名称的截取的方式,因为这样很容易就可以把一个exe文件伪装上传上去了。我们需要计算文件的类型。其实就是获得文件开头的一些字符进行判断。大家可以参考一下:

public class FileType {

	public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();

	private FileType() {
	}

	static {
		getAllFileType(); // 初始化文件类型信息
	}

	/**
	 * Discription:[getAllFileType,常见文件头信息]
	 */
	private static void getAllFileType() {
		FILE_TYPE_MAP.put("ffd8ffe000104a464946", "jpg"); // JPEG (jpg)
		FILE_TYPE_MAP.put("89504e470d0a1a0a0000", "png"); // PNG (png)
		FILE_TYPE_MAP.put("47494638396126026f01", "gif"); // GIF (gif)
		FILE_TYPE_MAP.put("49492a00227105008037", "tif"); // TIFF (tif)
		FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); // 16色位图(bmp)
		FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); // 24位位图(bmp)
		FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); // 256色位图(bmp)
		FILE_TYPE_MAP.put("41433130313500000000", "dwg"); // CAD (dwg)
		FILE_TYPE_MAP.put("3c21444f435459504520", "html"); // HTML (html)
		FILE_TYPE_MAP.put("3c21646f637479706520", "htm"); // HTM (htm)
		FILE_TYPE_MAP.put("48544d4c207b0d0a0942", "css"); // css
		FILE_TYPE_MAP.put("696b2e71623d696b2e71", "js"); // js
		FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); // Rich Text Format
															// (rtf)
		FILE_TYPE_MAP.put("38425053000100000000", "psd"); // Photoshop (psd)
		FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml"); // Email [Outlook
															// Express 6] (eml)
		FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "doc"); // MS Excel
															// 注意:word、msi 和
															// excel的文件头一样
		FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "vsd"); // Visio 绘图
		FILE_TYPE_MAP.put("5374616E64617264204A", "mdb"); // MS Access (mdb)
		FILE_TYPE_MAP.put("252150532D41646F6265", "ps");
		FILE_TYPE_MAP.put("255044462d312e350d0a", "pdf"); // Adobe Acrobat (pdf)
		FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb"); // rmvb/rm相同
		FILE_TYPE_MAP.put("464c5601050000000900", "flv"); // flv与f4v相同
		FILE_TYPE_MAP.put("00000020667479706d70", "mp4");
		FILE_TYPE_MAP.put("49443303000000002176", "mp3");
		FILE_TYPE_MAP.put("000001ba210001000180", "mpg"); //
		FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv"); // wmv与asf相同
		FILE_TYPE_MAP.put("52494646e27807005741", "wav"); // Wave (wav)
		FILE_TYPE_MAP.put("52494646d07d60074156", "avi");
		FILE_TYPE_MAP.put("4d546864000000060001", "mid"); // MIDI (mid)
		FILE_TYPE_MAP.put("504b0304140000000800", "zip");
		FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");
		FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
		FILE_TYPE_MAP.put("504b03040a0000000000", "jar");
		FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");// 可执行文件 一般禁止上传
		FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");// jsp文件
		FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");// MF文件
		FILE_TYPE_MAP.put("3c3f786d6c2076657273", "xml");// xml文件
		FILE_TYPE_MAP.put("494e5345525420494e54", "sql");// xml文件
		FILE_TYPE_MAP.put("7061636b616765207765", "java");// java文件
		FILE_TYPE_MAP.put("406563686f206f66660d", "bat");// bat文件
		FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");// gz文件
		FILE_TYPE_MAP.put("6c6f67346a2e726f6f74", "properties");// bat文件
		FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");// bat文件
		FILE_TYPE_MAP.put("49545346030000006000", "chm");// bat文件
		FILE_TYPE_MAP.put("04000000010000001300", "mxp");// bat文件
		FILE_TYPE_MAP.put("504b0304140006000800", "docx");// docx文件
		FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");// WPS文字wps、表格et、演示dps都是一样的
		FILE_TYPE_MAP.put("6431303a637265617465", "torrent");

		FILE_TYPE_MAP.put("6D6F6F76", "mov"); // Quicktime (mov)
		FILE_TYPE_MAP.put("FF575043", "wpd"); // WordPerfect (wpd)
		FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx"); // Outlook Express (dbx)
		FILE_TYPE_MAP.put("2142444E", "pst"); // Outlook (pst)
		FILE_TYPE_MAP.put("AC9EBD8F", "qdf"); // Quicken (qdf)
		FILE_TYPE_MAP.put("E3828596", "pwl"); // Windows Password (pwl)
		FILE_TYPE_MAP.put("2E7261FD", "ram"); // Real Audio (ram)
	}

	/**
	 * 得到上传文件的文件头
	 * 
	 * @param src
	 * @return
	 */
	private static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder();
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 根据制定文件的文件头判断其文件类型 纯文本文件如txt,不能判断。一般可以拒绝exe,bat等文件上传
	 * 
	 * @param file
	 * @return
	 */
	public static String getFileType(File file) {
		String res = null;

		InputStream is=null;
		try {
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return getFileType(is);

	}

	/**
	 * 根据制定文件的文件头判断其文件类型 纯文本文件如txt,不能判断。一般可以拒绝exe,bat等文件上传
	 * 
	 * @param inputStream 字节流对象
	 * @return
	 */
	public static String getFileType(InputStream inputStream) {
		String res = null;
		try {
			byte[] b = new byte[10];
			inputStream.read(b, 0, b.length);
			String fileCode = bytesToHexString(b);

			System.out.println(fileCode);

			// 这种方法在字典的头代码不够位数的时候可以用但是速度相对慢一点
			Iterator<String> keyIter = FILE_TYPE_MAP.keySet().iterator();
			while (keyIter.hasNext()) {
				String key = keyIter.next();
				if (key.toLowerCase().startsWith(fileCode.toLowerCase())
						|| fileCode.toLowerCase().startsWith(key.toLowerCase())) {
					res = FILE_TYPE_MAP.get(key);
					break;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return res;
	}

	public static void main(String[] args) throws Exception {

		File file = new File("d://abc.png");
		String type = getFileType(file);
		System.out.println("abc.txt : " + type);
		System.out.println();

	}
}