1. 1 不可撤销
  2. 2 小年兽 程嘉敏
  3. 3 手放开 李圣杰
  4. 4 迷人的危险3(翻自 dance flow) FAFA
  5. 5 山楂树之恋 程佳佳
  6. 6 summertime cinnamons / evening cinema
  7. 7 不谓侠(Cover 萧忆情Alex) CRITTY
  8. 8 神武醉相思(翻自 优我女团) 双笙
  9. 9 空山新雨后 音阙诗听 / 锦零
  10. 10 Wonderful U (Demo Version) AGA
  11. 11 广寒宫 丸子呦
  12. 12 陪我看日出 回音哥
  13. 13 春夏秋冬的你 王宇良
  14. 14 世界が终わるまでは… WANDS
  15. 15 多想在平庸的生活拥抱你 隔壁老樊
  16. 16 千禧 徐秉龙
  17. 17 我的一个道姑朋友 双笙
  18. 18 大鱼  (Cover 周深) 双笙
  19. 19 霜雪千年(Cover 洛天依 / 乐正绫) 双笙 / 封茗囧菌
  20. 20 云烟成雨(翻自 房东的猫) 周玥
  21. 21 情深深雨濛濛 杨胖雨
  22. 22 Five Hundred Miles Justin Timberlake / Carey Mulligan / Stark Sands
  23. 23 斑马斑马 房东的猫
  24. 24 See You Again Wiz Khalifa / Charlie Puth
  25. 25 Faded Alan Walker / Iselin Solheim
  26. 26 Natural J.Fla
  27. 27 New Soul Vox Angeli
  28. 28 ハレハレヤ(朗朗晴天)(翻自 v flower) 猫瑾
  29. 29 像鱼 王贰浪
  30. 30 Bye Bye Bye Lovestoned
  31. 31 Blame You 眠 / Lopu$
  32. 32 Believer J.Fla
  33. 33 书信 戴羽彤
  34. 34 柴 鱼 の c a l l i n g【已售】 幸子小姐拜托了
  35. 35 夜空中最亮的星(翻自 逃跑计划) 戴羽彤
  36. 36 慢慢喜欢你 LIve版(翻自 莫文蔚) 戴羽彤
  37. 37 病变(翻自 cubi) 戴羽彤
  38. 38 那女孩对我说 (完整版) Uu
  39. 39 绿色 陈雪凝
  40. 40 月牙湾 LIve版(翻自 F.I.R.) 戴羽彤
夜空中最亮的星(翻自 逃跑计划) - 戴羽彤
00:00 / 04:10

夜空中最亮的星 能否听清

那仰望的人 心底的孤独和叹息

夜空中最亮的星 能否记起

那曾与我同行 消失在风里的身影

我祈祷拥有一颗透明的心灵

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

那曾与我同行的身影 如今在哪里

夜空中最亮的星 是否在意

是等太阳先升起 还是意外先来临

我宁愿所有痛苦都留在心底

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

我祈祷拥有一颗透明的心灵

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

前后端分离项目文件下载解决方案

近期,接到一个新的需求,涉及到文件下载,因为我的服务全是发在内网的,项目又是完全的前后端分离的,前端服务通过nginx转发到外网,而且我的文件是传到内网文件服务器的,所以如何下载文件成为这个问题的难点。因为之前做过图片base64传输的需求,所以我首先想到的就是同时base64传输,然后前端将base64转成文件下载,查询了很多资料和博客,踩了很多坑,然后就有了这篇文章。

原理

先说思路,然后我们再贴代码,具体流程如下图:

用户发出文件访问请求时,nginx将请求转发至内网前端服务,然后前端服务访问后端接口,后端接口根据用户请求的文件名,请求文件服务器,并将文件转换成base64字符串返回给前端,前端将base64还原成文件,然后模拟下载。

代码实现

原理很简单,接下来我们看下如何实现:

前端代码

按照流程,我们先看前端代码,核心代码还是js:

 function clickBt() {
        console.log("网页加载完毕");
        $.ajax({
            type:"POST",
            url: "file/download",
            data: {fileName:"myfile.pdf"},
            success: function (data) {
             var downLoad =  $('#download');
                console.log("base64:" , data);
                console.log("msg:",data.msg)
                console.log("success:",data.success)
                if (data.success) {
                    var blob = b64toBlob(data.obj, "application/pdf");
                    console.log(blob);
                    var url = window.URL.createObjectURL(blob);
                    console.log('url:', url);
                    downLoad.attr("href", url);
                    dataURLtoFile(url, "test.pdf");
                } else {
                    alert("获取文件失败:", data.msg);
                }
            }
        });
    }
   /***
     * 下载文件
     * @param blobUrl:blob文件链接,例如:blob:http://localhost:8081/283340bd-f3c5-49d9-a3ac-b9e48ea08228
     * @param filename: 保存的文件名
     * */
    function dataURLtoFile(blobUrl, filename) {//将base64转换为文件
            var eleLink = document.createElement('a')
            eleLink.download = filename
            eleLink.style.display = 'none'
            eleLink.href = blobUrl
            // 触发点击
            document.body.appendChild(eleLink)
            eleLink.click()
            // 然后移除
            document.body.removeChild(eleLink);
    }

    /**
     * base64转Blob
     * @param b64Data:base64字符串,不含类型,如:JVBERi0xLjQKJeLjz9MKNCAwIG9iago8P……
     * @param contentType:类型,比如:"application/pdf"
     * @param sliceSize
     * @returns {Blob}
     */
    function b64toBlob(b64Data, contentType, sliceSize) {
        contentType = contentType || '';
        sliceSize = sliceSize || 512;

        var byteCharacters = window.atob(b64Data);
        console.log("byteCharacters:",byteCharacters)
        var byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            var slice = byteCharacters.slice(offset, offset + sliceSize);

            var byteNumbers = new Array(slice.length);
            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            var byteArray = new Uint8Array(byteNumbers);

            byteArrays.push(byteArray);
        }

        var blob = new Blob(byteArrays, { type: contentType });
        console.log("blob", blob)
        return blob;
    }

data类型

这里是文件类型:

序号 文件类型 data类型
1 txt data:text/plain;base64,
2 doc data:application/msword;base64,
3 docx data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,
4 xls data:application/vnd.ms-excel;base64,
5 xlsx data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,
6 pdf data:application/pdf;base64,
7 pptx data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,
8 ppt data:application/vnd.ms-powerpoint;base64,

如果是图片的额话:

序号 图片类型 data类型
1 png data:image/png;base64,
2 jpg data:image/jpeg;base64,
3 gif data:image/gif;base64,
4 svg data:image/svg+xml;base64,
5 ico data:image/x-icon;base64,
6 bmp data:image/bmp;base64,

后端代码

controller
@Api(value = "file", description = "文件管理")
@Controller
@RequestMapping("/file")
public class FileManagerController {

    @Value("${my.fileServer.url}") // http://127.0.0.1/file-Server/
    private String remotUrl;

    @RequestMapping("/download")
    @ResponseBody
    public AjaxJson downLoad(String fileName) {
        if(StringUtils.isEmpty(fileName)) {
            return new AjaxJson(new Exception("文件名不能为空"));
        }
        String base64 = Base64Util.remotePdfToBase64(remotUrl + fileName);
        AjaxJson result = new AjaxJson("请求成功", true);
        result.setObj(base64);
        return result;
    }

}

base64工具类:

public class Base64Util {
	private transient static Logger log = LoggerFactory.getLogger(Base64Util.class);

    /**
     * <p>将base64字符解码保存文件</p>
     * @param base64Code
     * @param targetPath
     * @throws Exception
     */
    public static void decoderBase64File(String base64Code,String targetPath) throws Exception {
        byte[] buffer = new BASE64Decoder().decodeBuffer(base64Code);
        FileOutputStream out = new FileOutputStream(targetPath);
        out.write(buffer);
        out.close();
    }

    /**
     *  将base64编码转换成PDF
     *  @param base64String
     *  1.使用BASE64Decoder对编码的字符串解码成字节数组
     *  2.使用底层输入流ByteArrayInputStream对象从字节数组中获取数据;
     *  3.建立从底层输入流中读取数据的BufferedInputStream缓冲输出流对象;
     *  4.使用BufferedOutputStream和FileOutputSteam输出数据到指定的文件中
     */
    public static void base64StringToPDF(String base64String, File file){
        BASE64Decoder decoder = new BASE64Decoder();
        BufferedInputStream bin = null;
        FileOutputStream fout = null;
        BufferedOutputStream bout = null;
        try {
            //将base64编码的字符串解码成字节数组
            byte[] bytes = decoder.decodeBuffer(base64String);
            //创建一个将bytes作为其缓冲区的ByteArrayInputStream对象
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            //创建从底层输入流中读取数据的缓冲输入流对象
            bin = new BufferedInputStream(bais);
            //创建到指定文件的输出流
            fout  = new FileOutputStream(file);
            //为文件输出流对接缓冲输出流对象
            bout = new BufferedOutputStream(fout);

            byte[] buffers = new byte[1024];
            int len = bin.read(buffers);
            while(len != -1){
                bout.write(buffers, 0, len);
                len = bin.read(buffers);
            }
            //刷新此输出流并强制写出所有缓冲的输出字节,必须这行代码,否则有可能有问题
            bout.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bout.close();
                fout.close();
                bin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * PDF转换为Base64编码
     * @param file
     * @return
     */
    public static String remotePdfToBase64(String file) {
        BASE64Encoder encoder = new BASE64Encoder();
        InputStream fin =null;
        BufferedInputStream bin =null;
        ByteArrayOutputStream baos = null;
        BufferedOutputStream bout =null;
        try {
            URL url = new URL(file);
            fin = url.openStream();
            bin = new BufferedInputStream(fin);
            baos = new ByteArrayOutputStream();
            bout = new BufferedOutputStream(baos);
            byte[] buffer = new byte[1024];
            int len = bin.read(buffer);
            while(len != -1){
                bout.write(buffer, 0, len);
                len = bin.read(buffer);
            }
            //刷新此输出流并强制写出所有缓冲的输出字节
            bout.flush();
            byte[] bytes = baos.toByteArray();
            return encoder.encodeBuffer(bytes).trim();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fin != null)
                    fin.close();
                if (bin != null)
                    bin.close();
                if (baos != null)
                    baos.close();
                if (bout != null)
                    bout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    public static void main(String[] args) {
        try {
            String base64Code =remotePdfToBase64("http://127.0.0.1/file-Server/myfile.pdf");
            log.info(base64Code);
            decoderBase64File(base64Code, "D://z2.pdf");
            base64StringToPDF(base64Code, new File("D://z3.pdf"));
            //toFile(base64Code, "D://z.pdf");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
}

总结

以上代码即可实现前后端分离项目得文件下载,如果需要兼容IE,需要考虑IE下兼容base64和blob文件格式,因为后来需要pdf文件预览,也就没有深入研究。随后我会发出pdf文件预览的相关解决方案,360兼容模式可用。

posted @ 2020-04-17 11:50  云中志  阅读(2887)  评论(0编辑  收藏  举报