文件上传编码问题

文件上传编码问题

1.编码问题的演示

我采用的是weblogic服务器,使用springmvc接收带文件的表单,content-type 为mutipart-formdata,页面采用的编码为UTF-8,但是项目采用的是GBK的编码。
从前端请求到服务器,也就是utf-8别编码成GBK,其实在这个过程中,就以及存在上传参数失真的情况。下面我用一个小demo显示出现问题的原因。

package com.example.demo.Controller;
import java.io.UnsupportedEncodingException;
public class TestDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println("我的去.xsl");
        byte[] b = "我的去.xsl".getBytes("UTF-8");
        for(int i=0;i

我的去.xsl
-26 -120 -111 -25 -102 -124 -27 -114 -69 46 120 115 108
鎴戠殑鍘�.xsl
-26 -120 -111 -25 -102 -124 -27 -114 63 46 120 115 108
我的�?.xsl
-26 -120 -111 -25 -102 -124 -17 -65 -67 63 46 120 115 108

文件上传编码问题_第1张图片
从上面的图可以看出,将utf转成gbk编码是就会有乱码的现象,gbk无法识别-69,所以用63代替。在从gbk到utf-8,自然会出现信息的丢失,而出现乱码的现象。

你好.xls
-28 -67 -96 -27 -91 -67 46 120 108 115
浣犲ソ.xls
-28 -67 -96 -27 -91 -67 46 120 108 115
你好.xls
-28 -67 -96 -27 -91 -67 46 120 108 115

文件上传编码问题_第2张图片从这张图可以看出,信息并没有丢失。那么上面时候会出现乱码呢,奇数个字符的时候是一定会出现乱码的,那么如何才能不乱码呢?

public class TestDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println("你燚.xls");
        byte[] b = "你燚.xls".getBytes("UTF-8");
        for(int i=0;i

你燚.xls
-28 -67 -96 -25 -121 -102 46 120 108 115
ä½ ç‡š.xls
-28 -67 -96 -25 -121 -102 46 120 108 115
你燚.xls
-28 -67 -96 -25 -121 -102 46 120 108 115

解决问题,保证字节正确才是硬道理,当调用getBytes(“utf-8”)转成字节数组后,创建iso-8859-1编码的字符串,iso-8858-1编码对应的一个字节对应的是一个字符因此不会出现最后一个字节的错误。

当然,我们从前端接收到参数,为了保证不乱码,我们可以js传的时候,用unicode编码,然后后台用unicode工具解码,
那么上面是unicode呢?
unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一且唯一的二进制编码,以满足跨平台,跨语言进行文本转化,处理的要求。

这里附上unicode的工具:

import java.io.UnsupportedEncodingException;

public class TestUnicode{

    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "简介";
        System.out.println(s+" --的unicode编码是:"+gbEncoding(s));
        System.out.println(gbEncoding(s) + " --转换成中文是:"+decodeUnicode(gbEncoding(s)));
        
        //System.out.println(gbEncoding(s) + " --转换成中文是:"+decodeUnicode("\\u7b80\\u4ecb"));
    }
    
    /*
     * 中文转unicode编码
     */
    public static String gbEncoding(final String gbString) {
        char[] utfBytes = gbString.toCharArray();
        String unicodeBytes = "";
        for (int i = 0; i < utfBytes.length; i++) {
            String hexB = Integer.toHexString(utfBytes[i]);
            if (hexB.length() <= 2) {
                hexB = "00" + hexB;
            }
            unicodeBytes = unicodeBytes + "\\u" + hexB;
        }
        return unicodeBytes;
    }
    /*
     * unicode编码转中文
     */
    public static String decodeUnicode(final String dataStr) {
        int start = 0;
        int end = 0;
        final StringBuffer buffer = new StringBuffer();
        while (start > -1) {
            end = dataStr.indexOf("\\u", start + 2);
            String charStr = "";
            if (end == -1) {
                charStr = dataStr.substring(start + 2, dataStr.length());
            } else {
                charStr = dataStr.substring(start + 2, end);
            }
            char letter = (char) Integer.parseInt(charStr, 16); // 16进制parse整形字符串。
            buffer.append(new Character(letter).toString());
            start = end;
        }
        return buffer.toString();
    }
}

2.使用request.getInputStream()解决编码问题

还有一种终极的方法,哈哈,这个方法可以解决所有的编码问题,一劳永逸,哈哈哈哈哈

package com.example.demo.utils;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class ResponseJSONUtils {
    public String getJsonStr(HttpServletRequest request) throws IOException {
        InputStream inputStream = request.getInputStream();
        ByteArrayOutputStream out=new ByteArrayOutputStream();

        byte[] buffer=new byte[1024];
        int len=0;

        while((len=inputStream.read(buffer))>0){
            out.write(buffer,0,len);
        }
        out.flush();
        byte[] result = out.toByteArray();
        String jsonStr= new String(result, "UTF-8");
        inputStream.close();
        out.close();
        return jsonStr;
    }
}

当页面的请求头为content-type为application/json的时候,用这种方法获取的就一定不会出现乱码的现象。

3. 使用multipartRequest.getInputStream()解决编码问题

当前端传一个带文件的表单的时候,这时候请求的contentType=multipart-formdata ,这个时候你会发现根本获取不到流文件,也就是说,request.getInputStream()=null;这是为什么呢?
原因就是,当传文件流到servlet的时候文件会被读取一次,而流只能读取一次之后就读取不到了,也就是关流了。所有我们z需要在初始化sevlet之前,就将文件流读取出来,并保存下来,以便于以后的读取。完成这个动作主要有三步:

(1),在web.xml中配置拦截器,将sevlet请求拦截下来
(2),创建一个类,实现Filter过滤器,重写flter里面的dofiler()的方法。包装httpServletRquest请求。
(3),创建一个类,继承httpServletRequestWrpper,重写里面的getInputStream的方法。其实就是装饰者设计模式,里面持有httpServletRequest的对象。

具体的代码实现步骤如下:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class HttpServletRequestReplacedFilter implements Filter{
    
    public void destroy() {
        
    } 

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;  
        if(request instanceof HttpServletRequest) {  
            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);  
        }
        if(null == requestWrapper) {  
            chain.doFilter(request, response);  
        } else {  
            chain.doFilter(requestWrapper, response);  
        }
    }
    
    /**
     * 初始化函数时,需要获取排除在外的url
     */
    public void init(FilterConfig config) throws ServletException {
    
    }
}

 
        requestFilter 
        com.sinosoft.sss.filter.HttpServletRequestReplacedFilter 
     
     
        requestFilter 
        /* 
    

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.web.bind.annotation.RequestMethod;

public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper{

    private final byte[] streamBody;
    private static final int BUFFER_SIZE = 4096;
    
	//对请求数据判断,getInputStream()为空的数据对key和value值进行拼接
    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        byte[] bytes = inputStream2Byte(request.getInputStream());
        if (bytes.length == 0 && RequestMethod.POST.name().equals(request.getMethod())) {
            //从ParameterMap获取参数,并保存以便多次获取
            bytes = request.getParameterMap().entrySet().stream()
                    .map(entry -> {
                        String result;
                        String[] value = entry.getValue();
                        if (value != null && value.length > 1) {
                            result = Arrays.stream(value).map(s -> entry.getKey() + "=" + s)
                                    .collect(Collectors.joining("&"));
                        } else {
                            result = entry.getKey() + "=" + value[0];
                        }

                        return result;
                    }).collect(Collectors.joining("&")).getBytes();
        }
        	//System.err.println(new String(bytes));
        streamBody = bytes;
    }

    private byte[] inputStream2Byte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[BUFFER_SIZE];
        int length;
        while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) {
            outputStream.write(bytes, 0, length);
        }

        return outputStream.toByteArray();
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(streamBody);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

好了,暂时就写这么多吧,这要对servlet的生命周期有一定的了解,行吧,886~~~~~~~~~~~~~

你可能感兴趣的:(java开发)