Java上传下载完全解析(二)

本文详细讲解了Java Web中的文件下载实现,通过HttpServletResponse获取文件输出流,结合文件路径实现下载。同时,文章介绍了如何在开发与生产环境中灵活切换文件存储路径,利用Spring的PropertyPlaceholderConfigurer和不同环境的.properties文件来配置文件上传和下载路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇我们分析了Java Web中的上传开发: Java上传下载完全解析(一) ,今天我们研究一下Java Web的下载与文件位置配置信息在开发与生产环境中的切换方法。


一、文件下载

  文件下载其实非常简单,首先我们根据请求的信息得到文件的名称,然后根据文件位置进行拼接得到具体的文件路径。然后我们只需要在HttpServletResponse中得到文件的输出流,并从文件系统中读取信息放到输出流即可。

  在工程中,首先我们会有一个文件的基础地址信息,比如为/data/dev,那么我在上传一个xx.txt文件时,上传时我会先得到其后缀,然后生成一个随机的文件名并加上其后缀,比如7eec46bcc65a21cbe293726eaa9175cf.txt,那么其最终地址为/data/dev/7eec46bcc65a21cbe293726eaa9175cf.txt,这样,上传时就将其保存到此路径下。

  同样的道理,下载的时候,需要告诉我需要下载的文件名为7eec46bcc65a21cbe293726eaa9175cf.txt,这样就可以得到其路径为/data/dev/7eec46bcc65a21cbe293726eaa9175cf.txt,然后就可以得到文件流并进行下载了。

  还有一点需要注意的是,假如我们下载的时候,假设地址为http://localhost:8080/download/xxx.txt,那么下载的地址前缀即为http://localhost:8080/download/,所以在上传的时候,得到文件名后,需要给客户端返回此文件的下载路径,比如文件名为7eec46bcc65a21cbe293726eaa9175cf.txt,那么返回给客户端的下载路径即为http://localhost:8080/7eec46bcc65a21cbe293726eaa9175cf.txt。

  下面贴代码,注意@RequestMapping("/download/{fileName:.+}")中最后必须加入.+,否则.txt .xml这些后缀是获取不到的:

  

    @RequestMapping("/download/{fileName:.+}")
    public void download(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception   {
  
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        //获取下载文件露肩
        String downLoadPath = filePath + fileName;
        //获取文件的长度
        long fileLength = new File(downLoadPath).length();
        //设置文件输出类型
        response.setHeader("Content-disposition", "attachment; filename="
                + new String(fileName.getBytes("utf-8"), "ISO8859-1"));
        //设置输出长度
        response.setHeader("Content-Length", String.valueOf(fileLength));
        //获取输入流
        bis = new BufferedInputStream(new FileInputStream(downLoadPath));
        //输出流
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }
        //关闭流
        bis.close();
        bos.close();
    }
下面为完整的上传下载的Controller代码:

package com.happyheng.controller;

import com.happyheng.entity.response.FileUploadResponse;
import com.happyheng.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

/**
 *
 */
@RestController
public class FileController {

    @Value("${file.path}")
    private String filePath;

    @Value("${file.download.path}")
    private String fileDownloadPath;


    @RequestMapping("test")
    public FileUploadResponse test(){
        FileUploadResponse uploadResponse = new FileUploadResponse();
        uploadResponse.setFilePath("test");
        return uploadResponse;
    }

    @ResponseBody
    @RequestMapping("/up")
    public FileUploadResponse upload(HttpServletRequest request) throws IllegalStateException, IOException, NoSuchAlgorithmException {

        String fileHttpPath = "";
        //创建一个通用的多部分解析器
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        //判断 request 是否有文件上传,即多部分请求
        if (multipartResolver.isMultipart(request)) {
            //转换成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            //取得request中的所有文件名
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                //记录上传过程起始时的时间,用来计算上传时间
                int pre = (int) System.currentTimeMillis();
                //取得上传文件
                MultipartFile file = multiRequest.getFile(iter.next());
                if (file != null) {
                    //取得当前上传文件的文件名称
                    String myFileName = file.getOriginalFilename();
                    //如果名称不为空,说明该文件存在,否则说明该文件不存在
                    if (!myFileName.trim().isEmpty()) {
                        System.out.println(myFileName);

                        String fileName = getRondomFileName() + getFileType(myFileName);
                        //定义本地路径
                        String path = filePath + fileName;
                        File localFile = new File(path);
                        file.transferTo(localFile);

                        fileHttpPath = fileDownloadPath + fileName;
                    }
                }
                //记录上传该文件后的时间
                int finaltime = (int) System.currentTimeMillis();
                System.out.println(finaltime - pre);
            }

        }
        FileUploadResponse uploadResponse = new FileUploadResponse();
        uploadResponse.setFilePath(fileHttpPath);
        return uploadResponse;
    }

    @RequestMapping("/download/{fileName:.+}")
    public void download(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception {

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        //获取下载文件露肩
        String downLoadPath = filePath + fileName;
        //获取文件的长度
        long fileLength = new File(downLoadPath).length();
        //设置文件输出类型
        response.setHeader("Content-disposition", "attachment; filename="
                + new String(fileName.getBytes("utf-8"), "ISO8859-1"));
        //设置输出长度
        response.setHeader("Content-Length", String.valueOf(fileLength));
        //获取输入流
        bis = new BufferedInputStream(new FileInputStream(downLoadPath));
        //输出流
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }
        //关闭流
        bis.close();
        bos.close();
    }


    private String getFileType(String fileName) {
        String[] splitText = fileName.split("[.]");
        if (splitText.length == 0) {
            return "";
        }
        return "." + splitText[splitText.length - 1];
    }

    private String getRondomFileName() throws NoSuchAlgorithmException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssSSS");
        StringBuilder builder = new StringBuilder(dateFormat.format(new Date()));
        builder.append((int) (Math.random() * 1000));

        return MD5Utils.getMD5(builder.toString());
    }

}


二、文件位置配置信息在开发与生产环境中的切换方法:

  我们在开发环境与生产环境中(为了简单,只说了两种环境),一般使用的文件上传下载路径是不同的,打一个比方,在dev下,文件的路径为/data/dev,在prod环境下,文件的路径为/data/prod,那么,怎么在两种环境中进行切换呢,我们可以使用PropertyPlaceholderConfigurer:

  首先在resources下面新建两个.properties文件。


其中dev下面文件为:

# file storage
file.path=/data/prod


# file download path
file.download.path=http://localhost:8080/download/


prod下面文件为:

# file storage
file.path=/data/prod


# file download path  
file.download.path=http://localhost:8080/download/


这样,然后在servlet-context.xml中设置:

    <!-- 环境配置文件扫描器 -->
    <beans profile="dev">
        <bean id="propertyConfigurer"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:dev/path.properties</value>
                </list>
            </property>
        </bean>
    </beans>


    <beans profile="prod">
        <bean id="propertyConfigurerProd"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:prod/path.properties</value>
                </list>
            </property>
        </bean>
    </beans>

这样,我们在web.xml中初始化DispatcherServlet时,就可以指定环境了:

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:servlet-context.xml</param-value>
    </init-param>
    <init-param>
      <param-name>spring.profiles.default</param-name>
      <param-value>dev</param-value>
    </init-param>
    <init-param>
      <param-name>spring.profiles.active</param-name>
      <param-value>prod</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  我们此时既指定了default环境,又指定了active环境,所以由于active环境优先级更高,所以使用prod环境,所以我们文件的下载路径为file.path=/data/prod


三、

  此项目的完整地址为 FileService ,里面会有上面源码中没有的一些工具类,欢迎大家下载,如果感觉有帮助的话,也可以star一下哦



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值