【软件应用开发】基于SSM框架的共享云盘系统设计与实现

本文编写目的:描述共享云盘系统的实现功能、运行环境以及系统的使用说明。
共享云盘系统完整代码:https://download.csdn.net/download/weixin_47936614/86608898

软件名称:共享云盘系统
软件开发环境/开发工具:操作系统win10、jdk1.8.0、eclipse-jee-oxygen-3a-win32-x86_64
软件的运行平台/操作系统:Windows 7及以上版本
软件架构:Spring + Spring MVC + MyBatis
开发语言:Java、HTML、CSS、JavaScript
软件运行支撑环境/支持软件:jdk1.8.0_202 MySQL Server 8.0 apache-tomcat-9.0.37

一.总体设计

1.需求概述

随着互联网技术的飞速发展和信息化建设的重视,越来越多的信息都以电子化、网络化的形式而存在。作为日常生活中数据涉及的沟通与存储工作,网络电子文档其价值正在日益提升。对于数据资源的存储很重要,那如何能够存储这些文件成为提高数据存储效率的关键问题,为了解决这一问题,本文设计开发的共享云盘系统可以对数据进行存储、共享,把数据存到云端,减少硬盘的使用,方便快捷地存储数据,共享数据文件。

2.系统结构

系统主要5个模块组成,分别为用户登录注册模块、文件模块、文件夹模块、回收站模块、分享模块。

  • 在用户登录注册模块中,用户可以注册账号,然后登录共享云盘系统,然后可以对自己的个人信息进行管理。
  • 在文件模块中,软件实现了三种功能,分别是文件上传下载、文件管理、文件在线查阅。在文件上传下载中,用户可以将本地的文件上传至自己的云盘,也可以从云盘中下载所需的文件,云盘的存储容量相应的发生变化。在文件管理中,用户可以对文件进行重命名、移动、删除操作,文件的路径以及云盘的容量相应的发生变化。在文件在线查阅中,用户可以在未下载云盘文件的情况下在线查看文件,能够查看文本、office文档、图片、音乐以及视频多种格式的文件。
  • 在文件夹管理模块中,软件实现了四种功能,分别是文件夹的创建、文件夹的重命名、文件夹的删除、文件夹的移动,用户可以对文件夹做相应的所需操作。
  • 在回收站模块中,软件实现了两种功能,分别是文件的删除、文件的还原。在文件的删除中,用户可以将文件删除,此时文件移动到回收站中,回收站会保存15天后自动删除过期的文件。在文件的还原中,用户也可以对所删除的文件进行找回操作,文件将从回收站中还原恢复至原存储路径。
  • 在分享模块中,软件实现了两种功能,分别是外链共享、删除共享。在外链共享中,用户可以将自己的文件生成共享链接分享给其他用户,同时用户可以查看到自己创建的共享外链列表。在删除共享中,用户可以删除自己所分享的文件,此时外链地址也相应地发生失效,其他用户不可访问该用户分享的文件。

系统结构如图1所示:
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第1张图片

图1 系统结构图

3.系统功能

  • 注册登录功能:用户通过输入账号、密码注册成为系统用户,并输入个人信息,然后输入账号、密码和验证码可以登录系统。
  • 文件功能:用户进行文件上传下载、文件管理、文件在线查阅。
  • 文件夹功能:用户可以进行文件夹的创建、文件夹的重命名、文件夹的删除、文件夹的移动。
  • 回收站功能:用户可以进行文件的删除、文件的还原。
  • 分享功能:用户可以共享文件和删除共享链接。

二.开发工具软件安装

1)下载并安装eclipse,从http://www.eclipse.org下载并安装eclipse
2)下载并安装MySQL,从https://www.mysql.com下载并安装MySQL。
3)下载并安装Tomcat,从https://tomcat.apache.org下载并安装Tomcat。
4)下载并安装OpenOffice,从http://www.openoffice.org下载并安装OpenOffice;安装OpenOffice成功后,进入安装目录启动OpenOffice服务。

三、系统功能介绍

1.系统主界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第2张图片

图2 系统主界面

2.注册登录功能

进入登录页面,如果该用户为新用户需要注册,点击注册进入注册页面并录入注册信息进行注册,点击注册将注册信息录入数据库中,注册完成。如果不需要注册,直接输入账号密码进行登录,若账号密码正确,则提示登录成功。如图3和图4所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第3张图片

图3 用户注册界面

(1)通过输入账号、密码和验证码来进行登录。
(2)用户输入账号、密码和验证码后,通过后台数据库检验其正确性,如果输入全部正确则进入系统首页,若其中有一个错误,则提示请重新填写用户信息。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第4张图片

图4 用户登录界面

3.文件上传下载功能

如果用户能成功登录,进入个人云盘主页,则其能够进行文件查看、管理、分享等功能,如图5所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第5张图片

图5 用户个人云盘主界面

在个人云盘主页面点击上传文件进行文件的上传,弹出文件浏览框,选择用户需要上传的文件,点击上传按钮,检查云盘剩余容量是否充足,若不足则无法上传,并且检查文件是否重复,若重复也无法上传,无法上传则提示上传失败返回主页面。满足条件则提示上传成功。勾选文件框选择需要的文件点击下载,弹出下载地址框,下载完成后提示下载成功。如图6和图7所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第6张图片

图6 文件上传界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第7张图片

图7 文件下载界面

4.文件管理功能

在个人云盘主页面点击上传文件进行文件上传,选择文件,可以对其进行重命名,如果有重复名称文件或者文件名有非法字符,则文件重命名失败。若无重复名称,文件名无非法字符则文件重命名成功。选择文件进行文件移动,选择想要移动到的位置,若无重复文件则移动成功。重复则无法移动。选择想要删除的文件,进行文件删除,确认是否删除,确认后提示删除成功。如图8、图9和图10所示。
在这里插入图片描述

图8 文件重命名界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第8张图片

图9 文件移动界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第9张图片

图10 文件删除界面

5.文件夹管理功能

新建文件夹时,创建成功时提示文件夹创建成功,选择文件夹对其进项文件夹重命名,重命名后检查文件夹命名是否重复,是否包含违法字符,若无重复且无违法字符,则提示文件夹重命名成功。选择文件夹进行文件夹删除,确认是否删除,确认后提示删除成功。选择文件夹对其进行移动,选择想要移动到的位置,若无重复名文件夹则移动成功。重复则无法移动。如图11、图12和图13所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第10张图片

图11 文件夹重命名界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第11张图片

图12 文件夹移动界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第12张图片

图13 文件夹删除界面

6.文件共享功能

在个人云盘主页面选中文件,选择分享文件如果分享成功则生成连接并且后台输出提取码供用户使用。分享成功之后有分享记录,可以选择删除分享。若分享不成功则返回主页面。如图14、图15所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第13张图片

图14 文件分享界面

在这里插入图片描述

图15 文件分享记录界面

其他用户可根据共享链接和提取码提取文件,然后可以下载文件。如图16、图17所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第14张图片

图16 输入提取码提取文件界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第15张图片

图17 文件提取成功界面

7.回收站功能

在个人云盘主页面点击回收站,显示回收站内文件信息,若点击清除回收站,进行确认是否清除,若确认清除则清空回收站并且刷新回收站页面。在回收站中勾选文件按钮,点击还原文件则将文件还原到云盘的原位置,并且提示还原成功。如图18、图19所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第16张图片

图18 清空回收站界面

在这里插入图片描述

图19 还原文件界面

8.文件在线预览功能

在个人云盘主页面选择文件判断文件的类型,若为图片、文本、音频、视频则直接显示文件内容。若为office文件,则获取百度云id,打开id对应的文档,可以在线查看文件内容。如图20、图21、图22、图23和图24所示。
【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第17张图片

图20 在线预览文本文件界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第18张图片

图21 在线预览图片文件界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第19张图片

图22 在线预览音频文件界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第20张图片

图23 在线预览视频文件界面

【软件应用开发】基于SSM框架的共享云盘系统设计与实现_第21张图片

图24 在线预览Office文档文件界面

四.部分代码实现

Doc2HtmlUtil.java

package cn.allene.yun.utils;

import java.io.File;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;

/**
 * 利用jodconverter(基于OpenOffice服务)将文件(*.doc、*.docx、*.xls、*.ppt)转化为html格式或者pdf格式,
 * 使用前请检查OpenOffice服务是否已经开启, OpenOffice进程名称:soffice.exe | soffice.bin
 */
public class Doc2HtmlUtil {
	private static Doc2HtmlUtil doc2HtmlUtil;

	/** * 获取Doc2HtmlUtil实例 */
	public static synchronized Doc2HtmlUtil getDoc2HtmlUtilInstance() {
		if (doc2HtmlUtil == null) {
			doc2HtmlUtil = new Doc2HtmlUtil();
		}
		return doc2HtmlUtil;
	}

	/*** 转换文件成pdf */
	public String file2pdf(InputStream fromFileInputStream, String toFilePath, String type) throws IOException {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		String timesuffix = sdf.format(date);
		String docFileName = null;
		String htmFileName = null;
		if (".doc".equals(type)) {
			docFileName = "doc_" + timesuffix + ".doc";
			htmFileName = "doc_" + timesuffix + ".pdf";
		} else if (".docx".equals(type)) {
			docFileName = "docx_" + timesuffix + ".docx";
			htmFileName = "docx_" + timesuffix + ".pdf";
		} else if (".xls".equals(type)) {
			docFileName = "xls_" + timesuffix + ".xls";
			htmFileName = "xls_" + timesuffix + ".pdf";
		} else if (".ppt".equals(type)) {
			docFileName = "ppt_" + timesuffix + ".ppt";
			htmFileName = "ppt_" + timesuffix + ".pdf";
		} else {
			return null;
		}
		File htmlOutputFile = new File(toFilePath + File.separatorChar + htmFileName);
		File docInputFile = new File(toFilePath + File.separatorChar + docFileName);
		if (htmlOutputFile.exists())
			htmlOutputFile.delete();
		htmlOutputFile.createNewFile();
		if (docInputFile.exists())
			docInputFile.delete();
		docInputFile.createNewFile();
		/*** 由fromFileInputStream构建输入文件 */
		try {
			OutputStream os = new FileOutputStream(docInputFile);
			int bytesRead = 0;
			byte[] buffer = new byte[1024 * 8];
			while ((bytesRead = fromFileInputStream.read(buffer)) != -1) {
				os.write(buffer, 0, bytesRead);
			}
			os.close();
			fromFileInputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 连接服务(端口号:8100)
		OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
		try {
			connection.connect();
			System.out.println("连接openoffice服务成功");
		} catch (ConnectException e) {
			System.err.println("文件转换出错,请检查OpenOffice服务是否启动。");
		}
		// convert 转换
		DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
		converter.convert(docInputFile, htmlOutputFile);
		System.out.println("转换成功");
		connection.disconnect();
		// 转换完之后删除word文件
		docInputFile.delete();
		return htmFileName;
	}
}

FileController.java

package cn.allene.yun.controller;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.multipart.MultipartFile;
import cn.allene.yun.pojo.FileCustom;
import cn.allene.yun.pojo.RecycleFile;
import cn.allene.yun.pojo.Result;
import cn.allene.yun.pojo.SummaryFile;
import cn.allene.yun.service.FileService;
import cn.allene.yun.service.UserService;
import net.sf.json.JSONObject;
@Controller
@RequestMapping("/file")
public class FileController {
	@Autowired
	private HttpServletRequest request;
	@Autowired
	private FileService fileService;
	@Autowired
	private UserService userService;
	/**
	 * 文件上传
	 * 
	 * @param files
	 *            文件名(多个)
	 * @param currentPath
	 *            当前路径
	 * @return Json对象
	 */
	@RequestMapping("/upload")
	public @ResponseBody Result<String> upload(
			@RequestParam("files") MultipartFile[] files, String currentPath) {
		try {
			fileService.uploadFilePath(request, files, currentPath);
		} catch (Exception e) {
			return new Result<>(301, false, "上传失败");
		}
		return new Result<String>(305, true, "上传成功");
	}
	/**
	 * 文件下载
	 * 
	 * @param currentPath
	 *            当前路径
	 * @param downPath
	 *            文件名
	 * @param username
	 *            用户名
	 * @return 文件下载流
	 */
	@RequestMapping("/download")
	public ResponseEntity<byte[]> download(String currentPath,
			String[] downPath, String username) {
		try {
			String down = request.getParameter("downPath");
			File downloadFile = fileService.downPackage(request, currentPath,
					downPath, username);
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			String fileName = new String(downloadFile.getName().getBytes(
					"utf-8"), "iso-8859-1");
			headers.setContentDispositionFormData("attachment", fileName);
			byte[] fileToByteArray = org.apache.commons.io.FileUtils
					.readFileToByteArray(downloadFile);
			fileService.deleteDownPackage(downloadFile);
			return new ResponseEntity<byte[]>(fileToByteArray, headers,
					HttpStatus.CREATED);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * 获取文件列表
	 * 
	 * @param path
	 *            路径
	 * @return Json对象
	 */
	@RequestMapping("/getFiles")
	public @ResponseBody Result<List<FileCustom>> getFiles(String path) {
		String realPath = fileService.getFileName(request, path);
		List<FileCustom> listFile = fileService.listFile(realPath);
		Result<List<FileCustom>> result = new Result<List<FileCustom>>(325,
				true, "获取成功");
		result.setData(listFile);
		return result;
	}
	/**
	 * 获取分享文件列表
	 * 
	 * @param path
	 *            路径
	 * @param username
	 *            用户名
	 * @return Json对象
	 */
	@RequestMapping("/getShareFiles")
	public @ResponseBody Result<List<FileCustom>> getFiles(String path,
			String username) {
		String realPath = fileService.getFileName(request, path, username);
		List<FileCustom> listFile = fileService.listFile(realPath);
		Result<List<FileCustom>> result = new Result<List<FileCustom>>(326,
				true, "获取成功");
		result.setData(listFile);
		return result;
	}
	/**
	 * 新建文件夹
	 * 
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件夹名
	 * @return Json对象
	 */
	@RequestMapping("/addDirectory")
	public @ResponseBody Result<String> addDirectory(String currentPath,
			String directoryName) {
		try {
			fileService.addDirectory(request, currentPath, directoryName);
			return new Result<>(336, true, "添加成功");
		} catch (Exception e) {
			return new Result<>(331, false, "添加失败");
		}
	}
	/**
	 * 删除文件夹
	 * 
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件夹名
	 * @return Json对象
	 */
	@RequestMapping("/delDirectory")
	public @ResponseBody Result<String> delDirectory(String currentPath,
			String[] directoryName) {
		try {
			fileService.delDirectory(request, currentPath, directoryName);
			return new Result<>(346, true, "删除成功");
		} catch (Exception e) {
			e.printStackTrace();
			return new Result<>(341, false, "删除失败");
		}
	}
	/**
	 * 重命名文件夹
	 * 
	 * @param currentPath
	 *            当前路径
	 * @param srcName
	 *            源文件名
	 * @param destName
	 *            目标文件名
	 * @return Json对象
	 */
	@RequestMapping("/renameDirectory")
	public @ResponseBody Result<String> renameDirectory(String currentPath,
			String srcName, String destName) {
		try {
			fileService
					.renameDirectory(request, currentPath, srcName, destName);
			return new Result<>(356, true, "重命名成功");
		} catch (Exception e) {
			return new Result<>(351, false, "重命名失败");
		}
	}
	@RequestMapping("/copyDirectory")
	public @ResponseBody Result<String> copyDirectory(String currentPath,
			String[] directoryName, String targetdirectorypath)
			throws Exception {
		try {
			fileService.copyDirectory(request, currentPath, directoryName,
					targetdirectorypath);
			return new Result<>(366, true, "复制成功");
		} catch (IOException e) {
			return new Result<>(361, true, "复制失败");
		}
	}
	/**
	 * 移动文件夹
	 * 
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件夹名
	 * @param targetdirectorypath
	 *            目标位置
	 * @return Json对象
	 */
	@RequestMapping("/moveDirectory")
	public @ResponseBody Result<String> moveDirectory(String currentPath,
			String[] directoryName, String targetdirectorypath) {
		try {
			fileService.moveDirectory(request, currentPath, directoryName,
					targetdirectorypath);
			return new Result<>(366, true, "移动成功");
		} catch (Exception e) {
			return new Result<>(361, true, "移动失败");
		}
	}
	/**
	 * 请求移动文件夹页面
	 * 
	 * @param model
	 *            模型
	 * @return 页面逻辑视图名
	 */
	@RequestMapping("/summarylist")
	/* 如果方法声明了注解@ResponseBody ,则会直接将返回值输出到页面。 */
	public String summarylist(Model model) {
		String webrootpath = fileService.getFileName(request, "");
		int number = webrootpath.length();
		SummaryFile rootlist = fileService.summarylistFile(webrootpath, number);
		model.addAttribute("rootlist", rootlist);
		return "summarylist";
	}
	/**
	 * 查找文件(模糊查询)
	 * 
	 * @param reg
	 *            要查找的文件名
	 * @param currentPath
	 *            当面路径
	 * @param regType
	 *            查找文件类型
	 * @return Json对象
	 */
	@RequestMapping("/searchFile")
	public @ResponseBody Result<List<FileCustom>> searchFile(String reg,
			String currentPath, String regType) {
		try {
			List<FileCustom> searchFile = fileService.searchFile(request,
					currentPath, reg, regType);
			Result<List<FileCustom>> result = new Result<>(376, true, "查找成功");
			result.setData(searchFile);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return new Result<>(371, false, "查找失败");
		}
	}
	/**
	 * 打开文件
	 * 
	 * @param response
	 *            响应文件流
	 * @param currentPath
	 *            当前路径
	 * @param fileName
	 *            文件名
	 * @param fileType
	 *            文件类型
	 */
	@RequestMapping("/openFile")
	public void openFile(HttpServletResponse response, String currentPath,
			String fileName, String fileType) {
		try {
			fileService.respFile(response, request, currentPath, fileName,
					fileType);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 请求音乐播放页面
	 * 
	 * @param model
	 * @param currentPath
	 *            当面路径
	 * @param fileName
	 *            文件名
	 * @return
	 */
	@RequestMapping("/openAudioPage")
	public String openAudioPage(Model model, String currentPath, String fileName) {
		model.addAttribute("currentPath", currentPath);
		model.addAttribute("fileName", fileName);
		return "audio";
	}
	/**
	 * 打开文档
	 * 
	 * @param currentPath
	 *            当面路径
	 * @param fileName
	 *            文件名
	 * @param fileType
	 *            文件类型
	 * @return Json对象(文件Id)
	 */
	@RequestMapping("/openOffice")
	public @ResponseBody Result<String> openOffice(String currentPath,
			String fileName, String fileType) {
		try {
			String openOffice = fileService.openOffice(request, currentPath,
					fileName);
			if (openOffice != null) {
				String pdfPath = (String) request.getSession().getAttribute(fileName);
				request.getSession().setAttribute("PDFID", openOffice);
				request.getSession().setAttribute(openOffice, pdfPath);
				Result<String> result = new Result<>(505, true, "打开成功");
				result.setData(openOffice);
				return result;
			}
			return new Result<>(501, false, "打开失败");
		} catch (Exception e) {
			e.printStackTrace();
			return new Result<>(501, false, "打开失败");
		}
	}
	/*--存储回收站所有删除文件信息,并返回给recycle.jsp--*/
	@RequestMapping("/recycleFile")
	public String recycleFile() {
		try {
			List<RecycleFile> findDelFile = fileService.recycleFiles(request);
			// 返回的删除文件若没有,则不设置findDelFile
			if (null != findDelFile && findDelFile.size() != 0) {
				request.setAttribute("findDelFile", findDelFile);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "recycle";
	}
	/*
	 * --删除回收站多个文件-- --获取当前路径以及文件名--
	 */
	@RequestMapping("/delRecycle")
	public @ResponseBody Result<String> delRecycleDirectory(int fileId[]) {
		try {
			fileService.delRecycle(request, fileId);
			return new Result<>(327, true, "删除成功");
		} catch (Exception e) {
			e.printStackTrace();
			return new Result<>(322, false, "删除失败");
		}
	}
	/* --清空回收站-- */
	@RequestMapping("/delAllRecycle")
	public @ResponseBody Result<String> delAllRecycleDirectory() {
		try {
			fileService.delAllRecycle(request);
			// 返回状态码
			return new Result<>(327, true, "删除成功");
		} catch (Exception e) {
			return new Result<>(322, false, "删除失败");
		}
	}
	/*
	 * --还原回收站文件-- --获取目的路径以及文件名--
	 */
	@RequestMapping("/revertDirectory")
	public @ResponseBody Result<String> revertDirectory(int[] fileId) {
		try {
			fileService.revertDirectory(request, fileId);
			return new Result<>(327, true, "还原成功");
		} catch (Exception e) {
			return new Result<>(322, false, "还原失败");
		}
	}
	@RequestMapping("/more")
	public String toMore() {
		return "more";
	}
}

FileService.java

package cn.allene.yun.service;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

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

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import cn.allene.yun.dao.FileDao;
import cn.allene.yun.dao.OfficeDao;
import cn.allene.yun.dao.UserDao;
import cn.allene.yun.pojo.FileCustom;
import cn.allene.yun.pojo.RecycleFile;
import cn.allene.yun.pojo.User;
import cn.allene.yun.pojo.SummaryFile;
import cn.allene.yun.utils.Doc2HtmlUtil;
import cn.allene.yun.utils.FileUtils;
import cn.allene.yun.utils.UserUtils;

@Service
public class FileService {
	/**
	 * 文件相对前缀
	 */
	public static final String PREFIX = "WEB-INF" + File.separator + "file" + File.separator;
	/**
	 * 新用户注册默认文件夹
	 */
	// RECYCLE = "recycle";
	public static final String[] DEFAULT_DIRECTORY = { "vido", "music", "source", "image", User.RECYCLE ,"office" };
	@Autowired
	private UserDao userDao;
	@Autowired
	private FileDao fileDao;

	/**
	 * 上传文件
	 * 
	 * @param request
	 * @param files
	 *            文件
	 * @param currentPath
	 *            当前路径
	 * @throws Exception
	 */
	public void uploadFilePath(HttpServletRequest request, MultipartFile[] files, String currentPath) throws Exception {
		for (MultipartFile file : files) {
			String fileName = file.getOriginalFilename();
			String filePath = getFileName(request, currentPath);
			File distFile = new File(filePath, fileName);
			if (!distFile.exists()) {
				file.transferTo(distFile);
				if ("office".equals(FileUtils.getFileType(distFile))) {
					try {
						String convertPath = filePath.substring(0, filePath.length() - 1) + "office";
						FileInputStream fileInputStream = new FileInputStream(distFile);
						Doc2HtmlUtil doc2HtmlUtil = Doc2HtmlUtil.getDoc2HtmlUtilInstance();
						String htmlName = null;
						String suffix = fileName.substring(fileName.lastIndexOf("."));
						htmlName = doc2HtmlUtil.file2pdf(fileInputStream, convertPath, suffix);
						String pdfPath = convertPath + "\\" + htmlName;
						request.getSession().setAttribute(fileName, pdfPath);
	
						officeDao.addOfficefile(FileUtils.MD5(distFile));
					} catch (Exception e) {
					}
				}
			}
		}
		reSize(request);
	}
	
	/**
	 * 上传文件(安卓接口)
	 * 
	 * @param request
	 * @param files
	 *            文件
	 * @param currentPath
	 *            当前路径
	 * @throws Exception
	 */
/*	public void uploadFilePathExt(HttpServletRequest request, MultipartFile file, String currentPath,String username) throws Exception {
			String fileName = file.getOriginalFilename();
			String filePath = getFileName(request, currentPath,username);
			File distFile = new File(filePath, fileName);
			if (!distFile.exists()) {
				file.transferTo(distFile);
				if ("office".equals(FileUtils.getFileType(distFile))) {
					try {
						String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
						String documentId = FileUtils.getDocClient().createDocument(distFile, fileName, suffix)
								.getDocumentId();
						officeDao.addOffice(documentId, FileUtils.MD5(distFile));
					} catch (Exception e) {
					}
				}
			}
		reSize(request);
	}*/

	/**
	 * 删除压缩文件包
	 * 
	 * @param downloadFile
	 */
	public void deleteDownPackage(File downloadFile) {
		if (downloadFile.getName().endsWith(".zip")) {
			downloadFile.delete();
		}
	}

	/**
	 * 下载文件打包
	 * 
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param fileNames
	 *            文件名
	 * @param username
	 *            用户名
	 * @return 打包的文件对象
	 * @throws Exception
	 */
	public File downPackage(HttpServletRequest request, String currentPath, String[] fileNames, String username)
			throws Exception {
		File downloadFile = null;
		if (currentPath == null) {
			currentPath = "";
		}
		if (fileNames.length == 1) {
			downloadFile = new File(getFileName(request, currentPath, username), fileNames[0]);//返回绝对路径名
			if (downloadFile.isFile()) {
				return downloadFile;
			}
		}
		String[] sourcePath = new String[fileNames.length];
		for (int i = 0; i < fileNames.length; i++) {
			sourcePath[i] = getFileName(request, currentPath, username) + File.separator + fileNames[i];
		}
		String packageZipName = packageZip(sourcePath);
		downloadFile = new File(packageZipName);
		return downloadFile;
	}

	/**
	 * 压缩文件
	 * 
	 * @param sourcePath
	 * @return
	 * @throws Exception
	 */
	private String packageZip(String[] sourcePath) throws Exception {
		String zipName = sourcePath[0] + (sourcePath.length == 1 ? "" : "等" + sourcePath.length + "个文件") + ".zip";
		ZipOutputStream zos = null;
		try {
			zos = new ZipOutputStream(new FileOutputStream(zipName));
			for (String string : sourcePath) {
				writeZos(new File(string), "", zos);
			}
		} finally {
			if (zos != null) {
				zos.close();
			}
		}
		return zipName;
	}

	/**
	 * 写入文件到压缩包
	 * 
	 * @param file
	 * @param basePath
	 * @param zos
	 * @throws IOException
	 */
	private void writeZos(File file, String basePath, ZipOutputStream zos) throws IOException {
		if (!file.exists()) {
			throw new FileNotFoundException();
		}
		if (file.isDirectory()) {
			File[] listFiles = file.listFiles();
			if (listFiles.length != 0) {
				for (File childFile : listFiles) {
					writeZos(childFile, basePath + file.getName() + File.separator, zos);
				}
			}
		} else {
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			ZipEntry entry = new ZipEntry(basePath + file.getName());
			zos.putNextEntry(entry);
			int count = 0;
			byte data[] = new byte[1024];
			while ((count = bis.read(data)) != -1) {
				zos.write(data, 0, count);
			}
			bis.close();
		}
	}

	/**
	 * 获取文件跟路径
	 * 
	 * @param request
	 * @return
	 */
	public String getRootPath(HttpServletRequest request) {
		String rootPath = request.getSession().getServletContext().getRealPath("/") + PREFIX;
		return rootPath;
	}

	/**
	 * 获取回收站跟路径
	 * 
	 * @param request
	 * @return
	 */
	public String getRecyclePath(HttpServletRequest request) {
		return getFileName(request, User.RECYCLE);
	}

	/**
	 * 获取文件路径
	 * 
	 * @param request
	 * @param fileName
	 * @return
	 */
	public String getFileName(HttpServletRequest request, String fileName) {
		if (fileName == null) {
			fileName = "";
		}
		String username = UserUtils.getUsername(request);//获取到了用户名
		return getRootPath(request) + username + File.separator + fileName;
	}

	/**
	 * 根据用户名获取文件路径
	 * 
	 * @param request
	 * @param fileName
	 * @param username
	 * @return
	 */
	public String getFileName(HttpServletRequest request, String fileName, String username) {
		if (username == null) {
			return getFileName(request, fileName);
		}
		if (fileName == null) {
			fileName = "";
		}
		return getRootPath(request) + username + File.separator + fileName;
	}

	/**
	 * 获取路径下的文件类别
	 * 
	 * @param realPath
	 *            路径
	 * @return
	 */
	public List<FileCustom> listFile(String realPath) {
		//对文件操作  需要new出一个文件,代表指向该文件内存地址
		File[] files = new File(realPath).listFiles();
		List<FileCustom> lists = new ArrayList<FileCustom>();
		if (files != null) {
			for (File file : files) {
				//RECYCLE = "recycle"
				if (!file.getName().equals(User.RECYCLE) && !file.getName().equals("office")) {
					FileCustom custom = new FileCustom();
					custom.setFileName(file.getName());//设置文件名
					custom.setLastTime(FileUtils.formatTime(file.lastModified()));//设置最后一次修改的时间
					/* 保存文件的删除前路径以及当前路径 */
					// custom.setFilePath(prePath);
					custom.setCurrentPath(realPath);//设置当前路径
					if (file.isDirectory()) {//如果表示的是目录,则返回true
						custom.setFileSize("-");//设置文件大小
					} else {
						custom.setFileSize(FileUtils.getDataSize(file.length()));//得到文件的大小
					}
					custom.setFileType(FileUtils.getFileType(file));//得到文件类型
					lists.add(custom);
				}
			}
		}
		return lists;
	}

	
	/**
	 * 获取路径下的文件类别
	 * 
	 * @param realPath
	 *            路径
	 * @return
	 */
	public List<FileCustom> listFileForApp(String realPath,HttpServletRequest request,String username) {
		String preFix = getRootPath(request) + username + File.separator; 
		//对文件操作  需要new出一个文件,代表指向该文件内存地址
		File[] files = new File(realPath).listFiles();
		List<FileCustom> lists = new ArrayList<FileCustom>();
		if (files != null) {
			for (File file : files) {
				if (!file.getName().equals(User.RECYCLE)) {
					FileCustom custom = new FileCustom();
					custom.setFileName(file.getName());
					custom.setLastTime(FileUtils.formatTime(file.lastModified()));
					/* 保存文件的删除前路径以及当前路径 */
					// custom.setFilePath(prePath);
					custom.setCurrentPath(realPath.replace(preFix, ""));
					if (file.isDirectory()) {
						custom.setFileSize("-");
						custom.setFileType("folder");
					} else {
						custom.setFileSize(FileUtils.getDataSize(file.length()));
						custom.setFileType("file");
					}
					lists.add(custom);
				}
			}
		}
		return lists;
	}
	//
	// public List searchFile(HttpServletRequest request, String
	// currentPath, String reg) {
	// List list = new ArrayList<>();
	// matchFile(list, new File(getFileName(request, currentPath)), reg, "");
	// return list;
	// }
	/**
	 * 查找文件
	 * 
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param reg
	 *            文件名字
	 * @param regType
	 *            文件类型
	 * @return
	 */
	public List<FileCustom> searchFile(HttpServletRequest request, String currentPath, String reg, String regType) {
		List<FileCustom> list = new ArrayList<>();
		matchFile(request, list, new File(getFileName(request, currentPath)), reg, regType == null ? "" : regType);
		return list;
	}

	private void matchFile(HttpServletRequest request, List<FileCustom> list, File dirFile, String reg,
			String regType) {
		File[] listFiles = dirFile.listFiles();
		if (listFiles != null) {
			for (File file : listFiles) {
				if (file.isFile()) {
					String suffixType = FileUtils.getFileType(file);
					if (suffixType.equals(regType) || (reg != null && file.getName().contains(reg))) {
						FileCustom custom = new FileCustom();
						custom.setFileName(file.getName());
						custom.setLastTime(FileUtils.formatTime(file.lastModified()));
						String parentPath = file.getParent();
						String prePath = parentPath.substring(
								parentPath.indexOf(getFileName(request, null)) + getFileName(request, null).length());
						custom.setCurrentPath(File.separator + prePath);
						if (file.isDirectory()) {
							custom.setFileSize("-");
						} else {
							custom.setFileSize(FileUtils.getDataSize(file.length()));
						}
						custom.setFileType(FileUtils.getFileType(file));
						list.add(custom);
					}
				} else {
					matchFile(request, list, file, reg, regType);
				}
			}
		}
	}

	/**
	 * 移动的文件列表
	 * 
	 * @param realPath
	 *            路径
	 * @param number
	 *            该路径下的文件数量
	 * @return
	 */
	public SummaryFile summarylistFile(String realPath, int number) {
		File file = new File(realPath);
		SummaryFile sF = new SummaryFile();
		List<SummaryFile> returnlist = new ArrayList<SummaryFile>();
		if (file.isDirectory()) {
			sF.setisFile(false);
			if (realPath.length() <= number) {
				sF.setfileName("yun盘");
				sF.setPath("");
			} else {
				String path = file.getPath();
				sF.setfileName(file.getName());
				//截取固定长度 的字符串,从number开始到value.length-number结束.
				sF.setPath(path.substring(number));
			}
			/* 设置抽象文件夹的包含文件集合 */
			for (File filex : file.listFiles()) {
				//获取当前文件的路径,构造该文件
				SummaryFile innersF = summarylistFile(filex.getPath(), number);
				if (!innersF.getisFile()) {
					returnlist.add(innersF);
				}
			}
			sF.setListFile(returnlist);
			/* 设置抽象文件夹的包含文件夹个数 */
			sF.setListdiretory(returnlist.size());

		} else {
			sF.setisFile(true);
		}
		return sF;
	}

	/**
	 * 新建文件夹
	 * 
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件夹名
	 * @return
	 */
	public boolean addDirectory(HttpServletRequest request, String currentPath, String directoryName) {
		File file = new File(getFileName(request, currentPath), directoryName);
		return file.mkdir();
	}

	/*--回收站显示所有删除文件--*/
	public List<RecycleFile> recycleFiles(HttpServletRequest request) throws Exception {
		//将本用户所有删除文件获取
		List<RecycleFile> recycleFiles = fileDao.selectFiles(UserUtils.getUsername(request));
		for (RecycleFile file : recycleFiles) {
			//一次实例化所有文件对象
			File f = new File(getRecyclePath(request), new File(file.getFilePath()).getName());
			//此时设置该文件名与文件最后删除的时间
			file.setFileName(f.getName());
			file.setLastTime(FileUtils.formatTime(f.lastModified()));
		}
		return recycleFiles;
	}

	/* 删除回收站的文件 */
	public void delRecycle(HttpServletRequest request, int[] fileId) throws Exception {
		for (int i = 0; i < fileId.length; i++) {
			//获取每个删除文件的id,同时获取该文件对象
			RecycleFile selectFile = fileDao.selectFile(fileId[i]);
			//根据每个删除文件的相对路径拼接绝对路径
			File testFile = new File(getRecyclePath(request), selectFile.getFilePath());
			String testFileName =testFile.getName();
			String relativePath= "\\" + testFileName;
			System.out.println(relativePath);
			File srcFile = new File(getRecyclePath(request), relativePath);
			//逐一删除数据库所存数据以及该文件
			fileDao.deleteFile(fileId[i], UserUtils.getUsername(request));
			delFile(srcFile);
		}
		reSize(request);
	}

	/*--依次遍历recycle下各个文件,并删除--*/
	public void delAllRecycle(HttpServletRequest request) throws Exception {
		//获取回收站中的所有文件
		File file = new File(getRecyclePath(request));
		//便利文件夹下所有文件
		File[] inferiorFile = file.listFiles();
		for (File f : inferiorFile) {
			delFile(f);
		}
		//根据用户进行删除
		fileDao.deleteFiles(UserUtils.getUsername(request));
		reSize(request);
	}

	/**
	 * 删除文件
	 * 
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件名
	 * @throws Exception
	 */
	public void delDirectory(HttpServletRequest request, String currentPath, String[] directoryName) throws Exception {
		for (String fileName : directoryName) {
			//拼接源文件的地址
			String srcPath = currentPath + File.separator + fileName;
			//根据源文件相对地址拼接 绝对路径
			File src = new File(getFileName(request, srcPath));
			File dest = new File(getRecyclePath(request));
			//调用commons  jar包中的moveToDirectory移动文件
			org.apache.commons.io.FileUtils.moveToDirectory(src, dest, true);
			fileDao.insertFiles(srcPath, UserUtils.getUsername(request));
			/*--将删除文件移动到recycle目录下*/
			// moveDirectory(request,currentPath,directoryName,User.RECYCLE);
		}
		//重新计算文件大小
		reSize(request);
	}

	/* 还原文件 */
	//难点2.还原文件时不等同于移动文件到,因为还原文件需要保存多个地址,而还原只单纯保存一个地址
	//而且还原时需要判断父子文件是否都删除了,此时就需要新建立父文件,再建立子文件,而commons.io.FileUtils
	//则可以很好的解决问题
	public void revertDirectory(HttpServletRequest request, int[] fileId) throws Exception {
		for (int id : fileId) {
			//根据要还原的文件id获得文件
			RecycleFile file = fileDao.selectFile(id);
			//获取该文件的文件名
			String fileName = new File(file.getFilePath()).getName();
			//根据文件名获取源文件地址
			File src = new File(getRecyclePath(request), fileName);
			//getFileName获取该文件删除时所保存的地址
			File dest = new File(getFileName(request, file.getFilePath()));
			org.apache.commons.io.FileUtils.moveToDirectory(src, dest.getParentFile(), true);
			fileDao.deleteFile(id, UserUtils.getUsername(request));
		}
	}

	/**
	 * 删除文件
	 * 
	 * @param srcFile
	 *            源文件
	 * @throws Exception
	 */
	private void delFile(File srcFile) throws Exception {
		/* 如果是文件,直接删除 */
		
		if (!srcFile.isDirectory()) {
			/* 使用map 存储删除的 文件路径,同时保存用户名 */
			boolean success = srcFile.delete();
			return;
		}
		System.out.println("文件是文件夹");
		/* 如果是文件夹,再遍历 */
		File[] listFiles = srcFile.listFiles();
		for (File file : listFiles) {
			if (file.isDirectory()) {
				delFile(file);
			} else {
				if (file.exists()) {
					file.delete();
				}
			}
		}
		if (srcFile.exists()) {
			srcFile.delete();
		}
	}

	/**
	 * 重命名文件
	 * 
	 * @param request
	 * @param currentPath
	 * @param srcName
	 * @param destName
	 * @return
	 */
	public boolean renameDirectory(HttpServletRequest request, String currentPath, String srcName, String destName) {
		//根据源文件名  获取  源地址
		File file = new File(getFileName(request, currentPath), srcName);
		//同上
		File descFile = new File(getFileName(request, currentPath), destName);
		return file.renameTo(descFile);
	}

	/**
	 * 新用户新建文件
	 * 
	 * @param request
	 * @param namespace
	 */
	public void addNewNameSpace(HttpServletRequest request, String namespace) {
		String fileName = getRootPath(request);
		File file = new File(fileName, namespace);
		file.mkdir();
		for (String newFileName : DEFAULT_DIRECTORY) {
			File newFile = new File(file, newFileName);
			newFile.mkdir();
		}
	}

	/**
	 * copy文件
	 * 
	 * @param srcFile
	 *            源文件
	 * @param targetFile目标文件
	 * @throws IOException
	 */
	private void copyfile(File srcFile, File targetFile) throws IOException {
		// TODO Auto-generated method stub
		if (!srcFile.isDirectory()) {
			/* 如果是文件,直接复制 */
			targetFile.createNewFile();
			FileInputStream src = (new FileInputStream(srcFile));
			FileOutputStream target = new FileOutputStream(targetFile);
			FileChannel in = src.getChannel();
			FileChannel out = target.getChannel();
			in.transferTo(0, in.size(), out);
			src.close();
			target.close();
		} else {
			/* 如果是文件夹,再遍历 */
			File[] listFiles = srcFile.listFiles();
			targetFile.mkdir();
			for (File file : listFiles) {
				File realtargetFile = new File(targetFile, file.getName());
				copyfile(file, realtargetFile);
			}
		}
	}

	public void copyDirectory(HttpServletRequest request, String currentPath, String[] directoryName,
			String targetdirectorypath) throws Exception {
		// TODO Auto-generated method stub
		for (String srcName : directoryName) {
			File srcFile = new File(getFileName(request, currentPath), srcName);
			File targetFile = new File(getFileName(request, targetdirectorypath), srcName);
			/* 处理目标目录中存在同名文件或文件夹问题 */
			String srcname = srcName;
			String prefixname = "";
			String targetname = "";
			if (targetFile.exists()) {
				String[] srcnamesplit = srcname.split("\\)");
				if (srcnamesplit.length > 1) {
					String intstring = srcnamesplit[0].substring(1);
					Pattern pattern = Pattern.compile("[0-9]*");
					Matcher isNum = pattern.matcher(intstring);
					if (isNum.matches()) {
						srcname = srcname.substring(srcnamesplit[0].length() + 1);
					}
				}
				for (int i = 1; true; i++) {
					prefixname = "(" + i + ")";
					targetname = prefixname + srcname;
					targetFile = new File(targetFile.getParent(), targetname);
					if (!targetFile.exists()) {
						break;
					}
				}
				targetFile = new File(targetFile.getParent(), targetname);
			}
			/* 复制 */
			copyfile(srcFile, targetFile);
		}
	}

	/**
	 * 移动文件
	 * 
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param directoryName
	 *            文件名
	 * @param targetdirectorypath
	 *            目标路径
	 * @throws Exception
	 */
	public void moveDirectory(HttpServletRequest request, String currentPath, String[] directoryName,
			String targetdirectorypath) throws Exception {
		// TODO Auto-generated method stub
		for (String srcName : directoryName) {
			File srcFile = new File(getFileName(request, currentPath), srcName);
			File targetFile = new File(getFileName(request, targetdirectorypath), srcName);
			/* 处理目标目录中存在同名文件或文件夹问题 */
			String srcname = srcName;
			String prefixname = "";
			String targetname = "";
			if (targetFile.exists()) {
				String[] srcnamesplit = srcname.split("\\)");
				if (srcnamesplit.length > 1) {
					String intstring = srcnamesplit[0].substring(1);
					Pattern pattern = Pattern.compile("[0-9]*");
					Matcher isNum = pattern.matcher(intstring);
					if (isNum.matches()) {
						srcname = srcname.substring(srcnamesplit[0].length() + 1);
					}
				}
				for (int i = 1; true; i++) {
					prefixname = "(" + i + ")";
					targetname = prefixname + srcname;
					targetFile = new File(targetFile.getParent(), targetname);
					if (!targetFile.exists()) {
						break;
					}
				}
				targetFile = new File(targetFile.getParent(), targetname);
			}
			/* 移动即先复制,再删除 */
			copyfile(srcFile, targetFile);
			delFile(srcFile);
		}
	}

	/**
	 * 递归统计用户文件大小
	 * 
	 * @param srcFile
	 *            位置
	 * @return
	 */
	private long countFileSize(File srcFile) {
		File[] listFiles = srcFile.listFiles();
		if (listFiles == null) {
			return 0;
		}
		long count = 0;
		for (File file : listFiles) {
			if (file.isDirectory()) {
				count += countFileSize(file);
			} else {
				count += file.length();
			}
		}
		return count;
	}

	/**
	 * 统计用户文件大小
	 * 
	 * @param request
	 * @return
	 */
	public String countFileSize(HttpServletRequest request) {
		long countFileSize = countFileSize(new File(getFileName(request, null)));
		return FileUtils.getDataSize(countFileSize);
	}

	/**
	 * 重新计算文件大小
	 * 
	 * @param request
	 */
	public void reSize(HttpServletRequest request) {
		String userName = UserUtils.getUsername(request);
		try {
			userDao.reSize(userName, countFileSize(request));
		} catch (Exception e) {
			e.printStackTrace();

		}
	}

	@Autowired
	private OfficeDao officeDao;

	/**
	 * 响应文件流
	 * 
	 * @param response
	 * @param request
	 * @param currentPath
	 *            当前路径
	 * @param fileName
	 *            文件名
	 * @param type
	 *            文件类型
	 * @throws IOException
	 */
	public void respFile(HttpServletResponse response, HttpServletRequest request, String currentPath, String fileName,
			String type) throws IOException {
		File file = new File(getFileName(request, currentPath), fileName);
		InputStream inputStream = new FileInputStream(file);
		if ("docum".equals(type)) {
			response.setCharacterEncoding("UTF-8");
			IOUtils.copy(inputStream, response.getWriter(), "UTF-8");
		} else {
			IOUtils.copy(inputStream, response.getOutputStream());
		}
	}

	/**
	 * 打开文档文件
	 * 
	 * @param request
	 * @param currentPath
	 * @param fileName
	 * @return
	 * @throws Exception
	 */
	public String openOffice(HttpServletRequest request, String currentPath, String fileName) throws Exception {
		System.out.println(new File(getFileName(request, currentPath), fileName));
		return officeDao.getOfficeId(FileUtils.MD5(new File(getFileName(request, currentPath), fileName)));
	}
}

共享云盘系统完整代码:https://download.csdn.net/download/weixin_47936614/86608898

你可能感兴趣的:(软件应用开发,java,spring,mvc,架构,eclipse)