zoukankan      html  css  js  c++  java
  • 文件的上传&预览&下载学习(一)

    注:主要是说明后端逻辑和数据库表设计

    1.当前主流的几种文件上传&预览&下载方式

    • 把文件直接存储在服务器
    • 分布式存储OSS,比如阿里OSS、Minio

    2.数据库表设计

    1. 由于文件都是跟业务关联的,比如评论里面掺杂评论图片,常规的设计就是在'评论表'添加上传'图片名称'字段和'图片相对路径',在上传成功后返回给前端
      1.1 如果是加入多个文件的话,这种设计就不太合适:10个文件在评论表就需要加20个字段
      1.2 针对'1.1'进行改进,建一种'文件信息表',设计字段'外键 fk_id'用来存储评论id(如果后续有商品图片也可以用这个存),这时候有需要考虑一个问题,id是根据什么生成的,是否唯一,如果是雪花ID就无需考虑这个问题,但如果是递增那极可能重复(商品id和评论id同时为1的情况);这时就还需要在'文件信息表'添加一个'外键类型 fk_type'作区分,比如评论就是comment,商品就是goods。然后把'图片名称'字段和'图片相对路径'放到'文件信息表'来(预览路径和下载路径可能不同,下载路径是绝对路径,预览路径为了防止用户直接访问服务器其他位置,做一层虚拟映射nginx)

    1.3 可能还需要加一个'文件下载名称',由于文件可能是'数字+字母(可能是md5)'的形式(如果是手机拍摄),然后要按照一定的规则比如'订单编号-产品名称-用户昵称'生成

    3.后端实现

    1.直接使用Java下载到指定目录(现根据外键查询出绝对路径,在下载)

    @Override
    public void downloadByOrderId(HttpServletResponse response, Long orderId, String fkType) throws IOException {
    	String fileLocation = null;
    	try{
    		// 一个订单只有一个报告pdf
    		QueryWrapper<FileUpload> fileUploadQueryWrapper = new QueryWrapper<>();
    		fileUploadQueryWrapper.eq("is_deleted","0");
    		fileUploadQueryWrapper.eq("fk_id",orderId);
    		fileUploadQueryWrapper.eq("fk_type",fkType);
    		FileUpload uploadEntity = uploadMapper.selectOne(fileUploadQueryWrapper);
    
    		response.setHeader("content-type", "application/octet-stream");
    		response.setContentType("application/octet-stream");        //这边可以设置文件下载时的名字,我这边用的是文件原本的名字,可以根据实际场景设置
    		response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(uploadEntity.getOldName(), "UTF-8"));
    
    		if(StrUtil.isNotBlank(uploadEntity.getLocation())){
    			fileLocation = uploadEntity.getLocation();
    		}
    		logger.info("fileLocation: "+fileLocation);
    
    		FileUtils.writeBytes(uploadEntity.getLocation(), response.getOutputStream());
    	}catch (Exception e){
    		logger.error("download error orderId: {} fkType: {} && fileLocation: {} msg: {}",orderId,fkType,fileLocation,e.getMessage());
    	}
    }
    
    public static void writeBytes(String filePath, OutputStream os) {
    	FileInputStream fi = null;
    	try {
    		File file = new File(filePath);
    		if (!file.exists()) {
    			throw new FileNotFoundException(filePath);
    		}
    		fi = new FileInputStream(file);
    		//TODO 扩大缓冲,多线程(请求的节点过多超时)
    		byte[] b = new byte[8192];
    		int length;
    		while ((length = fi.read(b,0,8192)) > 0) {
    			os.write(b, 0, length);
    		}
    	} catch (Exception e) {
    		e.printStackTrace();
    	} finally {
    		if(os != null) {
    			try {
    				os.close();
    			}catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    		if(fi != null) {
    			try {
    				fi.close();
    			}catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    2.分布式存储Minio,参考博客:https://www.freesion.com/article/5069848586/ (下载和上面的方式相同 根据文件名先去Minio查询,获取输入流,然后下载)

  • 相关阅读:
    C指针详解(经典,非常详细)
    PL/SQL错误提示 database character set(AL32UTF8) and Client character set(ZHS16GBK) are different
    安装Oracle报错,全部为未知!
    Oracle 支持在具有 DHCP 分配的 IP 地址的系统上进行安装
    Asp.Net页面生命周期
    oracle 导库建立测试库
    宝塔安装建站教程
    SEM理论
    SEM小总结
    SEM大致操作梳理
  • 原文地址:https://www.cnblogs.com/lhxBlogs/p/15606255.html
Copyright © 2011-2022 走看看