java发送url请求地址中有中文和特殊字符,java发送到前端的文件名存在特殊字符,java后端向前端发送blob类型、arraybuffer类型数据流,后端和前端解决方案

java发送url请求地址中有中文和特殊字符,java发送到前端的文件名存在特殊字符,java后端向前端发送blob类型、arraybuffer类型数据流,后端和前端解决方案

如果你的url请求地址没有中文和特殊字符,请移步到另一篇文章:
java后端向前端发送blob类型、arraybuffer类型数据流

下面详细说明项目情况:

1.前端请求图片跨域,考虑前端把这个图片地址发送到后端,后端请求到后通过数据流发送到前端。

2.测试环境一切ok,到了生产环境就崩了,晚上搞到12点没有好,第二天定位问题解决了。原因是生产环境中的url请求地址中有中文和特殊字符。TMD,中文可以理解,特殊字符过分了哦!
先看下这个url长什么样子:

http://10.4.124.195:9001/1/330211000000020570_20220105_210114_02_浙B12UX9_蓝_小车_000_{1875,1207,2734,1717}_{2355,1447,2494,1477}_2-1.jpg

这还是我为了演示简化后的。

遇到的坑:

1.开始这个url地址是通过get请求带参数传过来的,

http://XXX:8080/XXX?url=XXXXXXXXX

这是第一个坑,接到的参数url在我idea运行没问题的,但是springboot项目打了jar包,发布到windows server服务器或者linux服务器就会时不时出问题。什么原因:环境问题,编码方式不对,url通过urlcode编码传到后端就会可能出现编码问题
解决办法:换成post请求,传过来一个json,解析字符串总不会出问题了。

{"url":"http://10.4.124.195:9001/1/330211000000020570_20220105_210114_02_浙B12UX9_蓝_小车_000_{1875,1207,2734,1717}_{2355,1447,2494,1477}_2-1.jpg"}

2.我想拿着这个地址用java访问。报错:资源不存在 。地址没错,肯定是编码错误了,我不能url中文请求吧:

    @PostMapping(value = "/download")
    @ApiOperation(value = "图片请求")
    public void download(@RequestBody JSONObject object, HttpServletResponse response) throws IOException {
           String url = object.getString("url");
            URL connection = new URL(url );
            //打开链接
            HttpURLConnection conn = (HttpURLConnection)   connection.openConnection();
            //设置请求方式为"GET"
            conn.setRequestMethod("GET");
            //超时响应时间为5秒
            conn.setConnectTimeout(10 * 1000);
            }

3.urlcode编码后请求图片,还是资源不存在,找谁说理去。

    @PostMapping(value = "/download")
    @ApiOperation(value = "图片请求")
    public void download(@RequestBody JSONObject object, HttpServletResponse response) throws IOException {
           String url = object.getString("url");
             String urlencoded =
                    URLEncoder.encode(url, "utf-8");
            URL connection = new URL(urlencoded);
            //打开链接
            HttpURLConnection conn = (HttpURLConnection)   connection.openConnection();
            //设置请求方式为"GET"
            conn.setRequestMethod("GET");
            //超时响应时间为5秒
            conn.setConnectTimeout(10 * 1000);
            }

那我看看编码后地址变什么样了吧:

http%3A%2F%2F10.4.124.195%3A9001%2F1%2F330211000000020570_20220105_210114_02_%E6%B5%99B12UX9_%E8%93%9D_%E5%B0%8F%E8%BD%A6_000_%7B1875%2C1207%2C2734%2C1717%7D_%7B2355%2C1447%2C2494%2C1477%7D_2-1.jpg

牛啊,牛啊!它妈妈都不认识了。http的://都没了,

再改,替换特殊字符:

    @PostMapping(value = "/download")
    @ApiOperation(value = "图片请求")
    public void download(@RequestBody JSONObject object, HttpServletResponse response) throws IOException {
           String url = object.getString("url");
             String urlencoded =
                    URLEncoder.encode(url, "utf-8")
                            .replaceAll("%3A", ":")
                            .replaceAll("%2F", "/")
                            .replaceAll("%2C", ",")
                            .replaceAll("%7B", "{")
                             .replaceAll("%3F","?")
                            .replaceAll("%7D", "}");
            URL connection = new URL(urlencoded);
            //打开链接
            HttpURLConnection conn = (HttpURLConnection)   connection.openConnection();
            //设置请求方式为"GET"
            conn.setRequestMethod("GET");
            //超时响应时间为5秒
            conn.setConnectTimeout(10 * 1000);
            }

好了,请求到了,我开始也以为问题都解决了。其实只是解决了一半。

4.图片流拿到了,发送到前端时,后端报错:远程主机关闭了一个连接。诡异的是postman测试可以拿到文件流的。那就是问题在浏览器请求时的请求头、协议什么的有问题。最后排查到是文件流的文件名里面含有特殊字符。
什么原因:我把这个url地址当做文件名传给前端,可是url地址中有“,”,浏览器解析出错。终止了文件流传输。

真是什么情况都遇到了,找谁说理去!
解决办法:把这个文件名前后加上""括起来。

  //文件名有“,”等特殊字符发送到前端会报错,用""括起来解决
response.addHeader("Content-Disposition", "attachment;filename=\"" + urlencoded+"\"");

最终后端代码:

package com.neusoft.viic.server.dataService.controller;

import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

/**
 * @author dume
 * @create 2022-01-05 10:42
 **/
@Api(tags = "图片请求,前端blob类型或者arraybuffer类型接收")
@RestController
@RequestMapping("/image")
public class ImageController {
    private static final Logger log = LoggerFactory.getLogger("adminLogger");
    @PostMapping(value = "/download")
    @ApiOperation(value = "图片请求")
    public void download(@RequestBody JSONObject object, HttpServletResponse response) throws IOException {
        ServletOutputStream out =null;
        ByteArrayOutputStream baos = null;
        String url = object.getString("url");
        try {
            log.info("图片下载打印utf-8请求地址"+url);
            //将中文转成urlencode,将特殊字符转换回来,不然会请求异常,报错找不到资源
            String urlencoded =
                    URLEncoder.encode(url, "utf-8")
                            .replaceAll("%3A", ":")
                            .replaceAll("%2F", "/")
                            .replaceAll("%2C", ",")
                            .replaceAll("%7B", "{")
                             .replaceAll("%3F","?")
                            .replaceAll("%7D", "}");

            log.info("图片下载打印urlencoded请求地址"+urlencoded);
            //new一个URL对象
            URL connection = new URL(urlencoded);
            //打开链接
            HttpURLConnection conn = (HttpURLConnection) connection.openConnection();
            //设置请求方式为"GET"
            conn.setRequestMethod("GET");
            //超时响应时间为5秒
            conn.setConnectTimeout(10 * 1000);
            //通过输入流获取图片数据
            InputStream inStream = conn.getInputStream();
            //获取response输出流
            out = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            baos = new ByteArrayOutputStream();
            while ((len=inStream.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            //设置允许跨域的key
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            //文件名有“,”等特殊字符发送到前端会报错,用""括起来解决
            response.addHeader("Content-Disposition", "attachment;filename=\"" + urlencoded+"\"");
            //设置文件大小
            response.addHeader("Content-Length", "" + baos.size());
            //设置文件名,避免问题,这个也用""括起来
            response.setHeader("filename", "\"" + urlencoded+"\"");
            //设置文件类型
            response.setContentType("application/octet-stream");
            //向前端返回文件流
            out.write(baos.toByteArray());
        }catch (Exception e){
            e.printStackTrace();
            log.error(e.getMessage());
            throw e;
        }finally {
            try{
                baos.flush();
                out.flush();
                response.flushBuffer();
                baos.close();
                out.close();
            }catch (Exception e){
                e.printStackTrace();
                log.error(e.getMessage());
            }


        }


    }

}

最终前端代码:

图片接收:

//获取图片用于前端打包下载
var getFile = function(imgUrl) {
	return new Promise((resolve, reject) => {
		axios({
			method:'post',
			url: HOST_VIIC + 'image/download',
			headers: {
				'Content-Type': 'application/json'
			  },
			data:{
				'url':imgUrl
			  },
			responseType:'arraybuffer',		//这个参数必须加上,否则返回的图片打包后无法显示
			// responseType:'blob',	
		}).then(data => {
			resolve(data.data)
		}).catch(error => {
			reject(error.toString())
		})
	})
}

图片打包:

exportResultImage: function() {
      var self = this;
      this.imgLoading = this.$loading({
        lock: true,
        text: '图片下载中,请稍后!',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
			});
      const zip = new JSZip();
      const cache = {};
      const promises = [];
      //获取要导出图片的URL数组
      var data = [];
      const dataList = this.objectList;
      for ( var i = 0; i < dataList.length; i++ ) {
        var temp_data = {
          plateNo: dataList[i].PlateNo,
          passTime: dataList[i].PassTime,
          imgUrl: dataList[i].ObjectStoragePath
        }
        data.push(temp_data);
      }
      data.forEach(item => {
        const promise = this.viic.fileMng.getFile(item.imgUrl).then(data => { // 下载文件, 并存成ArrayBuffer对象
          const file_name = item.plateNo + "_" + item.passTime + ".jpg";
          zip.file(file_name, data, { binary: true }); // 逐个添加文件
          cache[file_name] = data;
        })
        promises.push(promise);
      })
			
      Promise.all(promises).then(() => {
        zip.generateAsync({type:"blob"}).then(content => { // 生成二进制流
          var filename = this.currentConfig.exportTitle + "图片列表.zip";
          FileSaver.saveAs(content, filename); // 利用file-saver保存文件
          self.imgLoading.close();
        })
      })
    },
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小七蒙恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值