Redis中间件的实战(一)——中转文件服务器2

89 阅读2分钟

介绍

前几天项目中碰到一个bug,人员列表的导出,有时能正常导出文件,有时文件导出会出错。
经过问题的排查分析,原来时系统用nginx负载均衡导致的问题。负载均衡有很多的策略,默认是轮询,轮询会导致,文件生成在服务器之后,下载的请求却不在同一个服务器,导致出现问题。还有一个是根据请求ip进行负载均衡,这样可以避免这个问题,但是ip策略有个问题,如果ip前三段的一致可能hash值是一致的,这样就达不到负载均衡的目的。在我们的项目中就发生了这个问题。所以只能采取利用外部中间件的形式来作为文件中继站。常用的是文件服务器,也可以使用redis或者mongodb,这里我介绍一下redis。

生产者——往redis中塞文件字符串(Base64编码)

  public void uploadFileToRedis(String filePath,String filename) throws FileNotFoundException {
      //根据文件路径取得文件对象;文件路径是生成文件的路径
        File file=new File(filePath);
//        Base64编码 (hutool的Base64)
        String base64File = Base64.encode(file);
        if (StringUtils.isNotEmpty(base64File)){
            //这里不设置过期时间,等到下载完成后在删除即可,这里我封装				了一个redis工具类
            //原生的为 		redisTemplate.opsForValue().set(filename,base64File)
            redisCache.setCacheObject(filename,base64File);
        }
    }

消费者——浏览器下载文件(从redis中将文件字符串取出,解码,然后转为文件流进行下载)

    @GetMapping("/download")
    public void fileDownload(String fileName HttpServletResponse response, HttpServletRequest request)
    {
        try
        {
//            从redis中取出文件   redisTemplate.opsForValue.get(fileName)
            String arrStr=redisCache.getCacheObject(fileName);
            if (StringUtils.isEmpty(arrStr)){
                throw new BusinessException("文件不存在");
            }

            // 设置文件下载的响应配置
            response.setCharacterEncoding("utf-8");
            response.setContentType("multipart/form-data");
            response.setHeader("Content-Disposition",
                    "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, +"文件名.xlsx"));

            // 将文件字符串用base64转码后转化为流,进行下载。 直接转为文件对象可以使用  Base64.decodeToFile(arrstr,destFile); destFile  目标File对象
            Base64.decodeToStream(arrStr, response.getOutputStream(), true);
        }
        catch (Exception e)
        {
           e.printStackTrace();
        }finally {
//            从redis中删除文件流 redisTemplate.remove(fileName)
            redisCache.deleteObject(fileName);
        }
    }

下面补充一下FileUtils工具类中的setFileDownloadHeader这个方法

/**
     * 下载文件名重新编码
     *
     * @param request 请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
            throws UnsupportedEncodingException
    {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
        {
            // 火狐浏览器
            filename = new String(fileName.getBytes(), "ISO8859-1");
        }
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        else
        {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }

至此关于redis作为文件中转服务器的应用就做完了,值得注意的是,如果文件中数据过大可能对redis的性能有一些影响,所以在下载完成后一定要及时的删除上一个文件字符串。