文件上传与下载2-基础文件上传与下载

文件上传

上传入参

import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class FileReq2 {
    /**
     * 文件
     */
    private MultipartFile files;
}

第三方上传入参

import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class FileReq {
    /**
     * 文件
     */
    private MultipartFile files;
    /**
     * 唯一编码
     */
    private String no;
    /**
     * 路径
     */
    private String path;
}

上传文件需要的依赖

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

1、上传-服务器把入参的文件保存到当前服务器

    //文件上传到当前服务器
    @SneakyThrows
    public String uploadToServer(FileReq2 req) {
   
        MultipartFile file = req.getFiles();
        Path path = Paths.get(req.getPath() + file.getOriginalFilename());
        //如果已经有同名文件,会抛异常
        Files.copy(file.getInputStream(), path);

		//生成一个随机数作为唯一键
		String no = "1111";
        //数据库保存path、no、文件名之类的数据
        return no;
    }

2、上传-服务器把入参的文件保存到第三方(http)

    //文件上传到第三方
    //使用Java进行HTTP请求时,以POST方式,并以form-data的形式发送MultipartFile(即文件数据)到第三方接口
    @PostMapping("/upload")
    public void uploadToYun(FileReq2 req) {
        //第三方的上传接口url
        String url = "http://xx.xx.xx.xx:xxxx/examine/job/uploaddd";

        File file1 = multipartFileToFile(req.getFiles());
        MultiValueMap<Object, Object> map = new LinkedMultiValueMap<>();
        FileSystemResource file = new FileSystemResource(file1.getPath());
        
        //假设表单字段名是files(需要按照第三方配置)
        map.add("files", file);
        //添加其他字段(需要按照第三方配置)
        //路径
        map.add("path","path");
        //唯一编码
        map.add("no",  "no");
        
        //此处应该把no、fullpath(path+文件名)保存到当前服务器数据库表里
        
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        //token需要按照第三方配置
        httpHeaders.add("token", "11d05e903cbb1f16681edee610923d45eb7");
        HttpEntity<MultiValueMap<Object, Object>> entity = new HttpEntity<>(map, httpHeaders);
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, entity, String.class);
        //上传成功之后,只会返回{"code":200,"msg":"操作成功","traceId":"","data":null}
        //上传之后的查看和下载就不好处理了
        //由此想到,实际上的三方保存文件接口,最少需要文件、路径/桶名称、唯一编码入参,当前服务器也要保存一份数据
    }

3、上传-服务器把入参的文件保存到第三方(feign)
直接调feign接口即可


下载文件

下载文件需要的依赖

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

1、下载-服务器把当前服务器文件返回给客户端(大文件、中等文件)

    //文件下载从当前服务器(大文件 几百MB-以上、中等文件 几十MB-几百MB)
    @SneakyThrows
    public void loadForServer(FileReq req, HttpServletResponse response) {
        //从上传时保存的数据,查出no对应的文件路径
        String no = req.getNo();
        String path = "/Users/duke/file/111.pdf";

        //设置响应头信息,告诉浏览器这是一个需要下载的文件
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        String name = new File(path).getName();
        response.setHeader("Content-Disposition", "attachment; filename=\"" + name + "\"");

        //读取文件内容并写入到输出流
        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(path));
             ServletOutputStream outputStream = response.getOutputStream()) {
            byte[] bytes = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, bytesRead);
            }
            //确保所有数据都被写出
            outputStream.flush();
        }
    }

2、下载-服务器把当前服务器文件返回给客户端(小文件、中等文件)

    //文件下载从当前服务器(小文件 几KB-几MB、中等文件 几十MB-几百MB)
    @SneakyThrows
    public ResponseEntity<byte[]> loadForServer(FileReq req) {
        //这一步是从数据库,查询查出no对应的文件全路径,上传文件时候保存的。没有实际落库所以写死值
        String no = req.getNo();
        String path = "/Users/duke/file/111.pdf";
        File file = new File(path);

        //设置响应头信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", file.getName());
        return new ResponseEntity<>(Files.readAllBytes(Paths.get(file.toURI())), headers, HttpStatus.OK);
    }

3、下载-服务器把其他文件返回给客户端(feign)
feign调用其他服务即可,返回一个url,url构成一般三方云服务相关要素拼接,比如域名+token+文件id等,点击url可以打开文件。

4、下载-特殊(重要!)
4、下载-特殊(重要!)
4、下载-特殊(重要!)

。。.。。4.1、场景
服务B保存文件并回调服务A,给了文件ID参数
但是由于文件要统一上传到服务C,并且服务B接口不作调整
只能服务A拿着文件ID从服务B下载文件,再上传到服务C
。。.。。4.2、问题
1、
服务B的接口限死了出参和入参

@GetMapping("/getFileById/{fileId}")
public void getFileById(@PathVariable String fileId, HttpServletResponse response) {}

2、
服务C的接口限死了出参和入参(req里面一个参数是MultipartFile[],并且试了之后发现外面的files和req里面是一样的,去掉外面的也没影响。搞不懂为啥要有俩)

@PostMapping("/upload")
@ResponseBody
public Response<List<UploadFileVO>> UploadFile(@RequestParam(value = "files") MultipartFile[] files, @Valid UploadReq req) {}

3、
服务A、服务B、服务C 使用feign调用
4、
不好处理的原因
。4.1、下载接口通常是提供给客户端直接访问的,服务会将文件内容直接写入HttpServletResponse的输出流中,没有出参
。4.2、Feign主要是为了处理JSON、XML等格式的响应体而设计的,当响应体是一个文件流时,这些库可能无法直接处理或解析它。

。。.。。4.3、解决


    public List<String> downloadAndUploadFile(String businessId, List<SigningFile> fileList) throws ApplicationException {

        List<MultipartFile> uploadFileList = new ArrayList<>();
        log.info("FileService.downloadAndUploadFile 下载文件开始,id:{},文件列表:{}", businessId, fileList);

        //下载文件
        for (int i = 0; i < fileList.size(); i++) {
            String fileName = fileList.get(i).getFileName() + "-" + i + ".pdf";
            log.info("FileService.downloadAndUploadFile 下载文件入参:{}", fileList.get(i).getFinishFileId());
            ResponseEntity<byte[]> entity = aClient.getFileByFileId(fileList.get(i).getFinishFileId());
            log.info("FileService.downloadAndUploadFile 下载文件出参:{}", entity);
            if (ObjectUtil.isEmpty(entity)) {
                continue;
            }
            if (entity.getStatusCode().is2xxSuccessful()) {
                byte[] fileResult = entity.getBody();
                if (null != fileResult && fileResult.length > 0) {
                    MultipartFile file = byteConvertFiles(fileResult, fileName, CommonConstant.FILE_PDF);
                    uploadFileList.add(file);
                }
            } else {
                log.error("文件下载失败(FileService.downloadAndUploadFile)");
            }
        }
        // 上传文件到文件服务
        log.info("FileService.downloadAndUploadFile 上传文件开始,id:{},文件列表长度:{}", businessId, uploadFileList.size());
        if (CollUtil.isEmpty(uploadFileList)) {
            return new ArrayList<>();
        }
        MultipartFile[] files = new MultipartFile[uploadFileList.size()];
        for (int i = 0; i < uploadFileList.size(); i++) {
            files[i] = uploadFileList.get(i);
        }
        log.info("FileService.downloadAndUploadFile-上传到文件中心入参files:{},id:{}", files, businessId);
        ResponseVo<List<UploadFileDTO>> vo = fileClient.upload(files, true, "");
        log.info("FileService.downloadAndUploadFile-上传到文件中心出参:{}", vo);

        if (ObjectUtil.isEmpty(vo.getData())) {
            return new ArrayList<>();
        }
        List<String> result = new ArrayList<>();

        List<UploadFileDTO> resultList = vo.getData();
        resultList.forEach(t -> result.add(t.getAttachNo()));
        return result;
    }

    //把字符串转成MultipartFile
    //这个方法是之前写的,直接复制来了,文件上传与下载1的转换应该也能用
    public MultipartFile byteConvertFiles(byte[] data, String fileName, String contentType) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        // AI释义:第三个参数设为true表示item是在内存中存储的(对于小文件这通常是可接受的),
        // AI释义:如果文件很大,你可能需要将其设置为false并在第四个参数中提供一个临时文件存储位置。
        //  好像第四个参数是文件名?
        FileItem item = factory.createItem("file", contentType, false, fileName);
        try (OutputStream os = item.getOutputStream()) {
            os.write(data);
            // 直接写入整个字节数组
        } catch (IOException e) {
            log.error("FileService.byteConvertFiles PDF文件流转换失败!", e);
            return null;
            // 转换失败时返回null或者抛出一个异常
        }
        return new CommonsMultipartFile(item);
    }

下载文件效果图
1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值