java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起

PART1:第一阶段:将上传的excel存储在服务器文件系统的一个绝对路径下,并在前台页面上提供导入导出按钮,能够把存在服务器某个路径下的excel表格等导出来以及把某些文件导入到服务器某个目录下存储起来,其中出现了部分问题并从问题当中学到了经验。

开发步骤:咱们玩的是maven或者说javaweb项目,那么步骤也就是那几步喽,导个jar包或者写个maven依赖坐标,写几个配置文件或者工具类或者XxxTemplate类,供咱们集成某个插件或者技术使用,然后SSM【前台–>controller–>service—>dao+entity+mybatis映射文件中的.xml文件,里面写了很多操作数据库的增删该查】,这不就一条路走通了嘛

  • 将上传的excel存储在服务器的一个绝对路径下,并提供下载按钮,能够把存在服务器某个路径下的excel表格等导出来。(在这里当时出现过一个很大的问题就是,之前我们就考虑到说只能存excel没考虑到客户有可能会把txt或者JPG照片等也传上去存下来,隔一段时间再取出来,后来就在项目的工具类中把txt呀、图片呀等都加进去了,就加了一个文件类型判断)
    java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第1张图片

整体前台效果图:
java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第2张图片

PART1-1:SSM中

  • step1:肯定是导入依赖jar包或者直接导入Maven依赖坐标
  • step2:前后台代码
    • 前台代码:当然,项目中肯定不止这一个或者这一点,不过我在网上找了能体现核心的例程代码,能体现出意思就行
      java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第3张图片
    • 后台代码:代码主要逻辑:
      • DownloadController.java【文件下载】

        • 所参照例程中的文件下载思路以及部分代码:
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第4张图片
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第5张图片
        • 实际项目中所用代码:
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第6张图片
        /**
         * Copyright (c) 2013-Now http://AIminminAI.com All rights reserved.
         */
        package com.xinhuize.internal.modules.sediment.web.sedimentdata;
        
        import java.io.BufferedInputStream;
        import java.io.BufferedOutputStream;
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.io.UnsupportedEncodingException;
        import java.util.List;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import javax.servlet.http.HttpSession;
        import org.springframework.stereotype.Controller;
        import org.springframework.ui.Model;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.servlet.mvc.support.RedirectAttributes;
        import com.xinhuize.internal.common.config.Global;
        import com.xinhuize.internal.common.web.BaseController;
        import com.xinhuize.internal.modules.sediment.entity.sedimentData.SedimentData;
        
        /**
         * 
         * @author HHB
         * @version 2021年8月28日
         */
        @Controller
        @RequestMapping("${adminPath}/sedimentdata/Download")
        public class DownloadController extends BaseController {
        	/**
        	 * 
        	 * @param session
        	 * @param response
        	 * @param sedimentData
        	 * @param request
        	 * @param redirectAttributes
        	 * @param model
        	 * @return
        	 * @throws IOException
        	 * @author HHB
        	 */
        	@SuppressWarnings("unused")
        	@RequestMapping("excelDownload")
        	public String excelDownload(HttpSession session, HttpServletResponse response, SedimentData sedimentData, HttpServletRequest request,
        			RedirectAttributes redirectAttributes, Model model) throws IOException {
        		//得到选择的年份,作为第一层文件夹名称
        		String sedSelectedYear = sedimentData.getSedSelectedYear();
        		//得到设备名称,作为第二层文件夹名称
        		String sedimentEquipmentName = sedimentData.getSedimentEquipmentName();
        		//得到表名,作为第三层文件夹名称
        		String sedimentTableName = sedimentData.getSedimentTableName();
        
        		//String filePath = "D:/xinhuize" + "saveExcel/" + sedSelectedYear + "/" + sedimentEquipmentName + "/" + sedimentTableName + "/";// 文件保存的第一层文件夹 ,组合而来
        		String filePath = "/usr/local/xinhuize-excel-save" + "/" + sedSelectedYear + "/" + sedimentEquipmentName + "/" + sedimentTableName + "/";// 文件保存的第一层文件夹 ,组合而来
        
        		File[] fileListDownLoad = new File(filePath).listFiles();
        		
        		if (fileListDownLoad == null) {
        			addMessage(redirectAttributes, "下载失败,请联系管理员");
        		}else {
        			for (File file : fileListDownLoad) {
        				if (file.isFile()) {
        					String name = file.getName();
        					/* 第二步:根据已存在的文件,创建文件输入流 */
        					InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
        					/* 第三步:创建缓冲区,大小为流的最大字符数 */
        					byte[] buffer = new byte[inputStream.available()]; // int available() 返回值为流中尚未读取的字节的数量
        					/* 第四步:从文件输入流读字节流到缓冲区 */
        					inputStream.read(buffer);
        					String fileName = file.getName();// 获取文件名
        					response.reset();
        					response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"), "utf-8"));
        					response.addHeader("Content-Length", "" + file.length());
        					/* 第六步:创建文件输出流 */
        					OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
        					if (file.isFile() && file.getName().endsWith(".xls")) {
        						response.setContentType("application/vnd.ms-excel");
        						response.setCharacterEncoding("utf-8");
        						setResponseHeader(response, fileName);
        						response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        						response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
        					}
        					response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        					response.setCharacterEncoding("utf-8");
        					setResponseHeader(response, fileName);
        					response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        					response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
        					/* 第七步:把缓冲区的内容写入文件输出流 */
        					outputStream.write(buffer);
        					/* 第八步:刷空输出流,并输出所有被缓存的字节 */
        					outputStream.flush();
        					/* 第九步:关闭输出流 */
        					outputStream.close();
        					/* 第五步: 关闭输入流 */
        					inputStream.close();
        
        				}
        			}
        			addMessage(redirectAttributes, "下载成功");
        		}
        		
        		return "redirect:" + Global.getAdminPath() + "/sedimentdata/sedimentData/exportWholeFile?repage";
        	}
        
        	/**
        	 * 发送响应流方法
        	 * @param response
        	 * @param fileName
        	 * @author HHB
        	 */
        	public static void setResponseHeader(HttpServletResponse response, String fileName) {
        		try {
        			try {
        				// 设置表文件名的字符编码,不然中文文件名会乱码
        				fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
        			} catch (UnsupportedEncodingException e) {
        				// TODO Auto-generated catch block
        				e.printStackTrace();
        			}
        			response.setContentType("application/octet-stream;charset=utf-8");
        			response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        			response.addHeader("Pargam", "no-cache");
        			response.addHeader("Cache-Control", "no-cache");
        		} catch (Exception ex) {
        			ex.printStackTrace();
        		}
        	}
        
        	/**
        	 * 获取指定文件夹下的所有文件名
        	 * @param path
        	 * @param listFileName
        	 * @author HHB
        	 */
        	public void getAllFileName(String path, List<String> listFileName) {
        		File file = new File(path);
        		String[] names = file.list();
        		if (names != null) {
        			String[] completNames = new String[names.length];
        			for (int i = 0; i < names.length; i++) {
        				completNames[i] = names[i];
        				listFileName.add(completNames[i]);
        			}
        		}
        	}
        
        	/**
        	 * 获取文件后缀名
        	 * @param filename
        	 * @return
        	 * @author HHB
        	 */
        	private String getfilenamelast(String filename) {
        		int start = filename.lastIndexOf(".");
        		if (start != -1) {
        			filename = filename.substring(start, filename.length());
        		}
        		return filename;
        	}
        }
        
        
      • UploadController.java
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第7张图片

        /**
         * Copyright (c) 2013-Now http://AIminminAI.com All rights reserved.
         */
        package com.xinhuize.internal.modules.sediment.web.sedimentdata;
        
        import java.io.File;
        import java.io.IOException;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
        import javax.servlet.http.HttpServletRequest;
        import org.springframework.stereotype.Controller;
        import org.springframework.ui.Model;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;
        import org.springframework.web.multipart.MultipartFile;
        import org.springframework.web.multipart.MultipartHttpServletRequest;
        import org.springframework.web.servlet.mvc.support.RedirectAttributes;
        import com.xinhuize.internal.modules.sediment.entity.sedimentData.SedimentData;
        
        /**
         * 0代表失败,1代表成功,2格式错误
         * @author HHB
         * @version 2021年8月27日
         */
        @Controller
        @RequestMapping("${adminPath}/sedimentdata/Upload")
        public class UploadController {
        	/**
        	 * 
        	 * @param request
        	 * @param sedimentData
        	 * @param redirectAttributes 用于结合addMessage,弹出上传成功的小弹幕字样
        	 * @param model 页面再一次刷新时,之前一次选好的的框中内容还在,不会随着刷新而没了
        	 * @return
        	 * @throws IOException
        	 * @author HHB
        	 */
        	@ResponseBody
        	@RequestMapping("excelUpload")
        	public String excelUpload(HttpServletRequest request, SedimentData sedimentData, RedirectAttributes redirectAttributes, Model model)
        			throws IOException {
        		String isSuccess = null;
        		Map<String, String> map = new HashMap<String, String>();
        		//得到选择的年份,作为第一层文件夹名称
        		String sedSelectedYear = sedimentData.getSedSelectedYear();
        		//得到设备名称,作为第二层文件夹名称
        		String sedimentEquipmentName = sedimentData.getSedimentEquipmentName();
        		//得到表名,作为第三层文件夹名称
        		String sedimentTableName = sedimentData.getSedimentTableName();
        		// 转型为MultipartHttpRequest  
        		//前台传输过来的上传的文件名
        		try {
        			MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        			List<MultipartFile> fileList = multipartRequest.getFiles("analysisFile");
        			for (MultipartFile mf : fileList) {
        				if (!mf.isEmpty()) {
        					try {
        						// 文件保存的第二层文件夹  ,以日期(年月日)命名
        						//String dataName = Tool.getyyyy();
        						//以上传时的文件名存储
        						String uploadFileName = mf.getOriginalFilename();
        						//String filePath = "D:/xinhuize" + "saveExcel/" + sedSelectedYear + "/" + sedimentEquipmentName + "/" + sedimentTableName + "/";// 文件保存的第一层文件夹 ,组合而来
        						String filePath = "/usr/local/xinhuize-excel-save" + "/" + sedSelectedYear + "/" + sedimentEquipmentName + "/" + sedimentTableName + "/";
        
        						File file = new File(filePath);
        						if (!file.exists()) {
        							file.mkdirs();
        							// 转存文件  三层路径,最后以上传时的文件名存储  
        							mf.transferTo(new File(filePath + uploadFileName));
        							isSuccess = "ok";
        						} else {
        							File[] listFiles = file.listFiles();
        							if (uploadFileName.endsWith(".xlsx") && file != null || listFiles != null || uploadFileName.endsWith(".xls")) {
        								for (int i = 0; i < listFiles.length; i++) {
        									if (listFiles[i].isFile()) {
        										listFiles[i].delete();
        									}
        									// 转存文件  三层路径,最后以上传时的文件名存储  
        									mf.transferTo(new File(filePath + uploadFileName));
        									isSuccess = "ok";
        								}
        							}
        						}
        					} catch (Exception e) {
        						map.put("isok", "0");
        						e.printStackTrace();
        					}
        				}
        			}
        		} catch (Exception e) {
        			e.printStackTrace();
        		}
        		//弹出上传成功的小弹幕字样
        		//return "redirect:"+Global.getAdminPath()+"/sedimentdata/sedimentData/exportWholeFile?repage";
        		return isSuccess;
        	}
        
        	/**
        	 * 获取文件后缀名
        	 * @param filename
        	 * @return
        	 * @author HHB
        	 */
        	private String getfilenamelast(String filename) {
        		int start = filename.lastIndexOf(".");
        		if (start != -1) {
        			filename = filename.substring(start, filename.length());
        		}
        		return filename;
        	}
        
        	/**
        	 * 
        	 * @param redirectAttributes
        	 * @param messages
        	 * @author HHB
        	 */
        	protected void addMessage(RedirectAttributes redirectAttributes, String... messages) {
        		StringBuilder sb = new StringBuilder();
        		for (String message : messages) {
        			sb.append(message).append(messages.length > 1 ? "
        "
        : ""); } redirectAttributes.addFlashAttribute("message", sb.toString()); } }
      • Tools.java

        /**
         * Copyright (c) 2013-Now http://AIminminAI.com All rights reserved.
         */
        package com.xinhuize.internal.modules.sediment.web.sedimentdata;
        
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.Random;
        
        import org.apache.log4j.Logger;
        
        /**
         * 
         * @author HHB
         * @version 2021年8月27日
         */
        public class Tool {
        	private static Logger log = Logger.getLogger(Tool.class);
            //获取日期
          public static String getyyyyMMdd(){
            Date d = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            return sdf.format(d);
          }
            //获取带毫秒时间戳
          public static String getyyyyMMddHHmmssSSS(){
            Date d = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            return sdf.format(d);
          }
            //获取10000-100000的随机数
          public static int getRandom(){
            int max=100000;
                int min=10000;
                Random random = new Random();
                int s = random.nextInt(max)%(max-min+1) + min;
                return s;
          }
          
          public static String getyyyy(){
        	    Date d = new Date();
        	    SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        	    return sdf.format(d);
        	  }
        }
        
        
      • 注意事项总结分析:

        • 先判断上传的文件是普通表单还是带文件的表单,用ServletFileUpload.isMultipartContent(request)
          • 获取ServletFileUpload:ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个Fileltem对象, 在使用ServletFileUpload对象解析请求时需要DiskFileltemFactory对象。所以,我们需要在进行解析工作前构造好DikFileltemFactory对象,通过ServletFileUpload对象的构造方法或setFileltemFactory(方法设置ServletFileUpload对象的fileltemFactory属性。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第8张图片
        • 创建上传文件的保存路径,就在WEB-INF下,如果没有则用文件流中的mkdir()方法创建这个目录
          在这里插入图片描述
        • 创建缓存,临时文件,假如文件大小超过了预期的大小,我们就把这个文件放到一个临时文件中然后过几天自动删除,获取提醒用户转存为永久
          在这里插入图片描述
        • 处理上传的文件,一般 都需要通过流来获取,我们可以使用request. getInputStream(),原生态的文件上传流获取:十分麻烦,所以不用这种方法
          • 但是我们都建议使用Apache的文件上传组件来实现,common-fileupload, 它需要依赖于commons -io组件,所以呢,咱们就要导jar包或者导入相关的maven依赖;
        • 创建DisFileItemFactory对象,处理文件上传路径或者文件大小限制
          在这里插入图片描述
        • 处理上传的文件
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第9张图片
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第10张图片

PART1-2:SpringBoot中:

  • 单个文件上传:
    • Spring File Storage,这个工具上传文件只要些许配置一行代码搞定。
      • Spring File Storage工具几乎整合了市面上所有的OSS对象存储平台,包括本地、FTP、SFTP、WebDAV、阿里云OSS、华为云OBS、七牛云Kodo、腾讯云COS、百度云 BOS、又拍云USS、MinIO、京东云 OSS、网易数帆 NOS等其它兼容 S3 协议的平台,只要在springboot中通过极简的方式就可以实现文件存储
      • pom.xml中引入必要的spring-file-storage.jar,比如本地和Aliyun OSS上传
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第11张图片
      • 如果要上传文件到OSS平台,需要引入对应平台的SDK包,将aliyun oss提供的变量配置到相应的模块上
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第12张图片
      • springboot启动类中增加注解@EnableFileStorage,显式的开启文件上传功能,到这就可以用了
        @EnableFileStorage // 文件上传工具
        @SpringBootApplication
        public class SpringbootFileStorageApplication {
        
            public static void main(String[] args) {
                SpringApplication.run(SpringbootFileStorageApplication.class, args);
            }
        }
        
      • 在业务类中引入FileStorageService服务,如下只要一行代码就可以完成文件上传
        @RestController
        public class FileController {
        
            @Autowired
            private FileStorageService fileStorageService;
        
            /**
             * 上传文件
             */
            @PostMapping(value = {"/upload"})
            public Object upload(MultipartFile file) {
                FileInfo upload  = fileStorageService.of(file).upload();
                return upload;
            }
        }
        
        • spring-file-storage还支持多种文件形式,URI、URL、String、byte[]、InputStream、MultipartFile,开发更加灵活;并且文件上传功能,更多时候我们都是在上传图片,那就会有动态裁剪图片、生成缩略图的需求,这些 spring-file-storage 都可以很容易实现。
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第13张图片
        • 我们还可以动态选择上传平台,配置文件中将所有平台开启,在实际使用中自由的选择
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第14张图片
          • 临时补充一个下载文件,可以直接根据文件url或者文件流下载
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第15张图片
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~文件上传与下载两阶段开发总结~整起_第16张图片
    • 编程步骤或者思路:
      • step1:本来学习一个新技术,就得创建一个demo来测试一下,但是咱们是要往项目中集成,所以就不用新创建一个SpringBoot项目了
      • step2:在pom.xml中引入模版引擎等依赖坐标
      • step3:前台那边肯定业务上,比如要搞个按钮呀之类的,处理好,
      • step4:然后后台这边就是:创建文件上传的处理控制器,命名为UploadController
      • step5:定义好配置文件相关信息,然后在Controller层中写相应代码
        spring.servlet.multipart.max-file-size=2MB//这个参数用于限制了上传文件的大小
        spring.servlet.multipart.max-request-size=2MB//用于限制了上传请求的大小
        file.upload.path=/Users/didi //file.upload.path是上面我们自己定义的用来保存上传文件的路径
        
  • 多个文件上传:
    • MultipartFile使用数组,参数名称files对应html页面中input的name,一定要对应
    • 后续处理文件的主体(for循环内)跟之前的一样,就是对MultipartFile数组通过循环遍历的方式对每个文件进行存储,然后拼接结果返回信息。

PART2:第二部分,就是用上了OSS,学习到了OSS相关的基础以及使用

  • 首先,得先到阿里云控制台,找OSS上面创建一些东西,我自己看作是用来存资源的容器,具体的网络上很多教程
    • 在对接一些OSS对象存储平台时,基本上就是一个平台一堆SDK代码
  • 然后就可以看到相应的工具栏以及文件夹,就直接可以上传文件并管理文件,勤看官方文档
  • 然后就是咱们那些老步骤,
    • 写Maven依赖坐标:
      <dependency>
          <groupId>com.aliyun.oss</groupId>
          <artifactId>aliyun-sdk-oss</artifactId>
          <version>2.8.3</version>
      </dependency>
      
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.4</version>
          <scope>provided</scope>
      </dependency>
      
      <dependency>
          <groupId>joda-time</groupId>
          <artifactId>joda-time</artifactId>
          <version>2.9.9</version>
      </dependency>
      
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.8.1</version>
      </dependency>
      
    • 安装一些插件之类的
    • 然后,就是咱们后端的重头戏,写代码呗
      • 阿里云OSS配置:在 resource 下新建一个 application-oss.properties
        aliyun.endpoint=oss-cn-beijing.aliyuncs.com
        aliyun.accessKeyId=LTAI5t9mpendCkALZKtBcqW1
        aliyun.accessKeySecret=XXX(你的accessKeySecret)
        aliyun.bucketName=malf-bucket
        aliyun.urlPrefix=https://malf-bucket.oss-cn-beijing.aliyuncs.com/
        spring.servlet.multipart.max-file-size=100MB
        spring.servlet.multipart.max-request-size=1000MB
        
      • 然后到阿里云的OSS概览页面以及其他地方看这些配置信息是啥,不能无脑抄网上
        • endpoint、bucketName、urlPrefix 在OSS概览页面就可以看到;
        • accessKeyId、accessKeySecret 需要在 Access Key 里面查看。
      • 配置类代码,以及controller、service等层代码

巨人的肩膀:
感谢csdn上有意无意被我看到的文章们,感谢这些作者,学习到了很多
https://blog.didispace.com/spring-boot-learning-21-4-3/
https://segmentfault.com/a/1190000040035818【OSS比较好的教程】
程序员小富老师
码哥字节老师
码农翻身
SpringForAll
javaGuide

你可能感兴趣的:(java,maven,开发语言,文件上传与下载,OSS)