文件管理

  • 0、需求及前言
  • 1、前端,上传按钮嵌入数据表格中
  • 2、利用IIS部署FTP文件服务器
  • 3、后台FTP连接和文件操作
  • 4、FTP遇到的问题和解决方案
  • 5、预览PDF文件V1.0:FTP+临时文件
  • 6、预览/下载文件V2.0:HTTP

0、需求及前言

我们的需求是实现文件的增删查改和预览功能。服务器有两台,一个是文件服务器,专门用来存储文件;另一个是用来部署项目的服务器。

小白啊,IO操作什么的基本没弄过,网络学的也不好,就搞这个操作,颇费心力。在网上扒了无数的帖子,换了很多个版本,最终还是实现了。总结下来其实也没有那么那么难,下面把关键的实现过程分享出来。不足之处请多指教。

前端框架:Layui
前端工具:pdf.js
协议:FTP、HTTP
后端:Springboot
需求:把上传、删除/替换按钮和预览下载功能放在数据表格中。文件上传至文件服务器。点击文件名时,对pdf文件进行预览,其他格式文件直接下载。
使用范围:内网用户(外网连接可以在此基础上另外了解)
最终效果:

1、前端,上传按钮嵌入数据表格中

这部分我写到了另一个博客,Layui 数据表格嵌套文件上传按钮,根据行数据id上传文件。

2、利用IIS部署FTP文件服务器

在网上找了两个教程,跟我当时设置的流程差不多:

  • 如何搭建FTP服务器实现文件的上传及下载?
  • Win10搭建ftp(含设置用户和密码)

只要在网页上输入ftp://192.168.xxx.xxx:端口,然后输入用户名和密码(如果有的话)可以看到文件列表,就说明部署成功了。

我遇到的问题:

  • 配置FTP服务时,登录后无法添加文件。
    错误信息:“将文件复制到FTP服务器时发生错误”
    解决办法: https://blog.csdn.net/hello_world_qwp/article/details/78717336

3、后台FTP连接和文件操作

在这里走了许多弯路。
网上有许多这种代码,大致是相同的,但是又有细微差别。我创建了一个工具类,便于其他controller调用。先把调试正常的代码放出来。遇到的问题后面会提到。

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import org.springframework.web.multipart.MultipartFile;import java.io.*;
import java.net.SocketException;
import java.util.Date;/*** @Author 27号白开水* @Date 2020/6/21 15:54*/
public class FtpUtil {private static Logger logger = Logger.getLogger(FtpUtil.class);//ftp服务器ip地址private static final String FTP_ADDRESS = "192.168.xxx.xxx";//端口号private static final int FTP_PORT = 2333;//用户名private static final String FTP_USERNAME = "upload";//密码private static final String FTP_PASSWORD = "123456";//本地字符编码private static String LOCAL_CHARSET = "GBK";// FTP协议里面,规定文件名编码为iso-8859-1private static final String SERVER_CHARSET = "ISO-8859-1";//附件路径,这里没用到//private static String FTP_BASEPATH = "";//连接ftp, 获取到FTPClient对象public static FTPClient getFTPClient(){FTPClient ftp = new FTPClient();try {int reply;ftp.connect(FTP_ADDRESS, FTP_PORT);//连接FTP服务器ftp.login(FTP_USERNAME, FTP_PASSWORD);//登录ftp.setConnectTimeout(50000);// 设置连接超时时间,5000毫秒if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())){logger.info("未连接到FTP,用户名或密码错误");ftp.disconnect();return ftp;}else {logger.info("FTP连接成功");}// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK)if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {LOCAL_CHARSET = "UTF-8";}ftp.setControlEncoding(LOCAL_CHARSET);//设置字符集编码方式}  catch (SocketException e) {e.printStackTrace();logger.info("FTP的IP地址可能错误,请正确配置");} catch (IOException e) {e.printStackTrace();logger.info("FTP的端口错误,请正确配置");}return ftp;}//关闭FTP方法public static boolean closeFTP(FTPClient ftp){try {ftp.logout();} catch (Exception e) {logger.error("FTP关闭失败");}finally{if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {ioe.printStackTrace();logger.error("FTP关闭失败");}}}return false;}//上传文件public static boolean uploadFile(FTPClient ftp, MultipartFile multipartFile, String filePath) throws IOException {//获取上传的文件流InputStream inputStream = multipartFile.getInputStream();String fileName = multipartFile.getOriginalFilename();boolean success = true;try {ftp.enterLocalPassiveMode();//设置被动传输ftp.setFileType(FTPClient.BINARY_FILE_TYPE);//设置文件传输模式为二进制,可以保证传输的内容不会被改变,ASC容易造成文件损坏String directory = filePath.substring(0, filePath.lastIndexOf("/") + 1);// 如果远程目录不存在,则递归创建远程服务器目录,这里是用于多层文件夹嵌套新建的情况,如果只有一层,那么只需要 1:跳转目录 2:不存在就新建if (!directory.equalsIgnoreCase("/") //忽略大小写进行比较&& !ftp.changeWorkingDirectory(new String(filePath.getBytes(LOCAL_CHARSET),SERVER_CHARSET))) {int start = 0;int end = 0;if (directory.startsWith("/")) {start = 1;} else {start = 0;}end = directory.indexOf("/", start);//查询除开头“/”之外的第一个“/”的位置while (true) {String subDirectory = filePath.substring(start, end);if (!ftp.changeWorkingDirectory(subDirectory)) {//跳转子目录if (ftp.makeDirectory(new String(subDirectory.getBytes(LOCAL_CHARSET),SERVER_CHARSET))) {//新建子文件夹ftp.changeWorkingDirectory(subDirectory);//再次尝跳转子目录} else {System.out.println("创建目录失败");success = false;return success;}}start = end + 1;end = directory.indexOf("/", start);// 检查所有目录是否创建完毕if (end <= start) {break;}}}//跳转目标目录ftp.changeWorkingDirectory(filePath);success = ftp.storeFile(new String(fileName.getBytes(LOCAL_CHARSET),SERVER_CHARSET), inputStream); //存储if(success){logger.info("上传成功");}else{logger.error("上传失败");}} catch (IOException e) {e.printStackTrace();logger.error("上传失败");} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}return success;}//替换文件(实际是先删除后上传)public static Boolean replaceFile(MultipartFile file, String filePath, String fileName) throws IOException {Boolean success = false;FTPClient ftpClient = getFTPClient();deleteFile(ftpClient, filePath,fileName); //删除文件uploadFile(ftpClient, file, filePath);closeFTP(ftpClient);return success;}//删除文件public static Boolean deleteFile(FTPClient ftpClient, String filePath, String fileName){boolean flag = false;//转移至目标目录try {ftpClient.changeWorkingDirectory(new String(filePath.getBytes(LOCAL_CHARSET), SERVER_CHARSET));//跳转目录flag = ftpClient.deleteFile(new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET));//删除文件if (!flag) {throw new Exception("FTP附件删除失败!");}} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return flag;}
}

controller或者service调用就是这样:

//文件路径
String filePath = "/2020/";//路径不包含文件名
//调用自定义的FTP工具类上传文件
FTPClient ftpClient = FtpUtil.getFTPClient();
Boolean success = FtpUtil.uploadFile(ftpClient, multipartFile, filePath);//调用工具类上传
System.out.println(success? "上传成功": "上传失败");
FtpUtil.closeFTP(ftpClient);

我这里只是简单地用到了连接、上传、删除,还有5 中一个获取文件流,其他操作可以参考以下两位大佬的代码:

  1. JAVA FTPClient FTP简单操作
  2. java上传、下载、预览、删除ftp服务器上的文件

4、FTP遇到的问题和解决方案

  1. FtpClient.storeFile返回false
    这个问题网上的解决方案一般是因为没有设置服务器被动连接模式,或者是因为中文文件名问题,可以参考这个回答:https://www.cnblogs.com/xiangpiaopiao2011/archive/2012/02/28/2371679.html
    但是我没有解决。
    我在公司是用笔记本连内网wifi的,可以创建文件夹,但是文件传输就是false。在网上找了半天,很多回答都是更改成被动模式,但我这边不适用。后来同事测试了一下发现他的可以传文件上去???
    残念。
    后来考虑到操作文件的人(公司内部用)都是用的内网网线,就直接用这个版本的代码没有再改了。
  2. 文件夹和文件乱码的问题
    解决办法:
    1、开启服务器对UTF-8的支持,但是有的服务器是不支持UTF-8的,这时就只好用本地GBK的编码。
    2、FTP协议规定文件名编码为iso-8859-1,所以上传的文件目录或文件名需要转码。
    就是上面带有new String(xxxx.getBytes(LOCAL_CHARSET),SERVER_CHARSET) 的那些。
  3. 目标目录已创建,但是文件没有传到目录中去,而是传到了根目录上
    这仍然是编码的问题导致的。加上那句new String(xxxx.getBytes(LOCAL_CHARSET),SERVER_CHARSET) 就好了。

预览的问题困扰了我很久,因为不知道怎么实现。网上的教程大多是从服务器本地拿文件,少了文件服务器,实现方式跟我这不一样。后来看到pdf.js这个插件,只要获取到文件输入流就可以实现预览。然后在获取文件流上又弯弯绕绕一大圈。中间的心路历程堪称心酸,吃了没文化的苦。

这里我尝试了两个版本,一是FTP+临时文件的版本,二是HTTP版本。方法一在生成临时文件时耗费时间,有些影响用户体验。推荐使用HTTP。(这里因为工期有点紧了,上传那里没有再学习HTTP方法,日后有机会再更新)(用FTP不用临时文件应该也是可行的,不过我没找到合适的方法)。

5、预览PDF文件V1.0:FTP+临时文件

pdf.js的用法很简单,就是下载,然后放到项目静态文件目录中,可以参考这两个文章:
https://blog.csdn.net/semial/article/details/89510312
https://blog.csdn.net/qq_36537546/article/details/105793577
最终的实现过程是这样的:先从FTP获取文件流,在本地生成一个临时文件,然后用pdf.js渲染临时文件。
现在想来,其实关键在于获取临时文件,pdf.js的用处只是渲染的更好看一些。
下面是关键(所有)代码:
js:

//表头渲染,使点击文件名即可预览
, {field: 'file_name', title: '文件名称', width: 480, templet: function (d) {if (d.file_name != null && d.file_name != '' && d.file_name != undefined){return '<a href="javascript:void(0);" style="color: #0B9EB0;size: 15px;" lay-event="detail">'+d.file_name+'</a>';}else {return '未上传';}}
}//对行操作进行监听,调用pdf.js打开临时文件
window.open("/static/js/mes/fileManagement/web/viewer.html?file=" //前半句是pdf.js的viewer.html的路径+ encodeURIComponent("/allFiles/showDetail?filePath="+filePath));//后半句是controller的注解路径加传参,filePath是文件服务器路径+文件名。//然后对URL进行编码,否则会因为出现两个问号而报错

Controller:

@RequestMapping("/showDetail")public void showDetail(String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("文件查看" + filePath);// 编辑请求头部信息// 解决请求头跨域问题(IE兼容性 也可使用该方法)response.setHeader("Access-Control-Allow-Origin", "*");response.setContentType("application/pdf");FTPClient ftpClient = FtpUtil.getFTPClient();FileInputStream inputStream = FtpUtil.getStream(ftpClient, filePath);byte[] data = null;data = new byte[inputStream.available()];inputStream.read(data);response.getOutputStream().write(data);inputStream.close();FtpUtil.closeFTP(ftpClient);}

FtpUtil工具类:

//获取预览需要的文件流信息
public static FileInputStream getStream(FTPClient ftpClient, String filePath) throws IOException {//filePath是文件夹名加文件名//在客户端本地生成一个临时文件File tempFile = new File("E:/","mesTempFile.pdf");//将预览文件放到临时文件OutputStream outputStream = new FileOutputStream(tempFile);ftpClient.retrieveFile(new String(filePath.getBytes(LOCAL_CHARSET), SERVER_CHARSET),outputStream);outputStream.close();//读取临时文件的文件流FileInputStream fileInputStream = new FileInputStream(tempFile);return fileInputStream;
}

6、预览/下载文件V2.0:HTTP

这里参见我的另一篇博客

====================================
发布时间2020.06.29
更新时间2020.08.05

====================================

基本内容就是这些。
不足之处,请多指教。

Springboot + layui + FTP文件上传删除 + HTTP文件下载预览 + pdf.js文件预览(项目实战总结)相关推荐

  1. java struts2 excel上传_文件上传方法,使用Struts2,实现Excel文件读取并写入数据库技术...

    文件上传方法,使用Struts2,实现Excel文件读取并写入数据库技术 如题:文件信息的批量导入-- 项目中经常会遇到客户的一些单表信息的数据批量导入,也就是提供定制Excel表,再把Excel表中 ...

  2. node 生产的env文件怎么注入_前端各种文件上传攻略,从小图片到大文件断点续传...

    写在前面 今年国庆假期终于可以憋在家里了不用出门了,不用出去看后脑了,真的是一种享受.这么好的光阴怎么浪费,睡觉.吃饭.打豆豆这怎么可能(耍多了也烦),完全不符合我们程序员的作风,赶紧起来把文章写完. ...

  3. 写给新手前端的各种文件上传攻略,从小图片到大文件断点续传

    写在前面 今年国庆假期终于可以憋在家里了不用出门了,不用出去看后脑了,真的是一种享受.这么好的光阴怎么浪费,睡觉.吃饭.打豆豆这怎么可能(耍多了也烦),完全不符合我们程序员的作风,赶紧起来把文章写完. ...

  4. 前端各种文件上传攻略,从小图片到大文件断点续传

    写在前面 今年国庆假期终于可以憋在家里了不用出门了,不用出去看后脑了,真的是一种享受.这么好的光阴怎么浪费,睡觉.吃饭.打豆豆这怎么可能(耍多了也烦),完全不符合我们程序员的作风,赶紧起来把文章写完. ...

  5. springboot 删除路径下面所有文件_springboot文件上传删除下载

    SpringBoot文件上传.删除及下载 最近的项目中,需要将文件保存项目的根目录路径下,特此记录下文件的操作: 文件上传/*** 文件上传(相对路径) * *@paramuploadFile 文件 ...

  6. springboot以FTP方式上传文件到远程服务器

    此处远程服务器是ubuntu,关于ftpserver的配置请参考该文https://blog.csdn.net/sunxiaoju/article/details/85224602,在此处就不再赘述. ...

  7. html文件上传删除,文件上传以及删除文件方法

    文件上传公共方法 1.[代码][Java]代码 import java.io.File; import java.util.Date; import javax.servlet.http.HttpSe ...

  8. 小程序对七牛云文件上传删除批量删除生成token封装无需服务器一个小程序搞定

    微信小程序获取token接入七牛云上传删除批量删除图片封装亲测可用 小程序获取七牛云uptoken删除文件封装 在研究官方文档后自己用小程序生成uptoken上传凭证封装,其他资料都说要服务器我又没钱 ...

  9. 【文件上传绕过】——前端检测_前端js验证漏洞

    文章目录 一.实验目的: 二.工具: 三.实验环境: 四.实验目的: 五.漏洞说明: 1. 漏洞原理: 2. `js前端验证`过程代码: 六.实验过程: 1. 判断是否存在前端`js绕过漏洞`: 1. ...

最新文章

  1. Mycat分片规则详解
  2. 为什么RStudio Server这么慢?
  3. 范道馨晟宾馆还不错!
  4. 数据结构实验之链表三:链表的逆置
  5. gsoap生成webservice调用客户端接口
  6. 【Linux sshfs】sshfs将远程目录挂载到本地目录
  7. 我为什么用docker-compose来打包开发环境
  8. 计算机文档我的文档丢失,恢复我的电脑窗口中共享文档与我的文档不见了的方法...
  9. Java线程更新ui_android使用多线程更新ui示例分享
  10. Hadoop概念学习系列之谈谈RPC(三十三)
  11. python画好看的图-python如何画出漂亮的地图?
  12. mysql++缓冲区_思考mysql内核之初级系列4--innodb缓冲区管理(摘自老杨)
  13. 用 servlet 来创建一个用户登录界面
  14. MSDEV.EXE 版本
  15. 怎么关闭计算机硬件加速,win7关闭硬件加速的方法,手把手抓图教你如何关闭硬件加速功能...
  16. 一键磨皮插件:DR5白金版(支持ps 2022)中文版
  17. 银行业务中台这么搞,新产品上线提速60%
  18. 史上首例!程序员写的代码,被国家博物馆收藏了!
  19. 数据科学分布——二项式分布
  20. AoCoder 1983 [AGC001E] BBQ Hard(组合数+dp)

热门文章

  1. C语言多case自动跳出,C语言switch中case后跟随break语句
  2. 读书笔记-----《平凡的世界》第五篇
  3. 白话科普:如何训练ChatGPT,能用它来挣钱吗?【P.S. 今晚19:30,说透ChatGPT】
  4. SAP参数记忆功能失效
  5. SAML2.0 笔记(二)
  6. 计算机联机玩游戏的操作方法,茶杯头大冒险怎么联机 茶杯头大冒险游戏联机方法介绍-游侠网...
  7. socketpair的理解
  8. 规则引擎实战篇-------银行贷款业务处理1
  9. Python_EasyGui图形化的安装,配置窗口,简单实战(登录界面,猜数字游戏,模拟记事本,统计代码量)
  10. linux系统 修改文件权限