Web开发&文件上传下载及编码解码

java开发离不开变量,其中最让人头疼的莫过于不同位置的文件或字符串读取时造成的乱码问题,尤其是涉及到文件操作时,乱码尤为常见。

1、什么是编码和解码

不管什么类型的数据,什么格式的文件,在内存中存储形态都是二进制码,也就是bit流,当人为的以八位为一个字节规定后,就成了一般的比特流(byte字节),对于java来说,不涉及位层面的操作,因此数据操作的最小单位一般都是byte。
比特流不能直观的看到内容,因此我们需要把比特流转变为字符流,字符我们是能够看懂的,这个从比特流转变为字符流的过程就叫做编码,那么相反的,从字符流转换为比特流的过程就叫做解码
比特流的是由许多个八位bit构成的,每个bit位要么是0要么是1,因此很好表示,但是如果想将比特流转变为字符流就要困难多了,因为世界上字符太多,一个比特只有八位,最多最多只能对应2的8次方减1个不同的字符,这完全不够,因此大多时候需要用一个至多个比特来表示一个字符,这就是编码,也就是说,编码本质上是一种“读”比特流的方式,比特流没有被改变。
而解码呢,是将字符流再转换成比特流,将每一个字节都再“翻译”成一个或多个比特,解码和编码互为逆过程。
对于一段比特流,若自身不变化,当它先编码再解码,如果编码和解码同属一种方式(例如isolates8859-1,UTF-8,GBK等),那么解码后与编码前比特流应该不会有变化。
对于文件传输来说,在在主机A上新建编辑文件时,会将编辑好的字符流解码为比特流存储在计算机硬盘上(除了编辑的内容外还会加上文件头等部分构成文件),当需要传输时,是将比特流通过网络传输到另外的主机B(在网络上传输时,传输的只能是比特流),然后存储在B的硬盘上,如果想在主机B上看到该文件,需要把传输过来的比特流以某种方式编码为字符流,然后供我们来查看。
什么是乱码,乱码就是读取文件时的编码方式和新建文件时解码方式不同,导致你编辑文件时字符流不能正确的显示出来。
且我们通常不会过于区分编码解码,将编码解码的方式统一称为编码方式,以这种情况来说:乱码是编码方式不同造成

2、java中的字符串

在java中,每个字符占用两个字节,因此每个中文也是占用一个char,这种方式使得字符串操作时可以忽略编码问题。进行字符串读取拼接时,只需要考虑结果,不用在意过程。
java中编码和解码对应的操作分别为:
编码:new String(byte[] bytes,String charset);
解码:string.getBytes( );或string.getBytes(String charset);
以一个例子来看,这是以GBK方式写的java文件。
这是结果:
这里写图片描述
这是源代码:
这里写图片描述
以结果来看:(序号表示行号)
1、首先是以java字符串存储的两个字符:”中文“;
2、把该字符串以默认方式解码(文件编码方式为GBK,因此默认解码方式也是GBK),可以看到两个字符占用了四个字节:-42 -48 -50 -60
3、把”中文“两个字符以默认方式解码(可以得到2的结果),再以UTF-8方式编码,得到一堆乱码
4、把”中文“两个字符以UTF-8方式解码,可以看到两个字符会占用六个字节:-28 -72 -83 -26 -106 -121
5、6、这两行以GBK方式解码和编码,结果不会有改变的。
7、8、这两行同3和4,只是iso-8859-1作为单字节编码,无论如何都不会显示出汉字的。
9、以GBK方式先解码再编码,结果可以正常显示
10、以UTF-8方式先解码再编码,结果可以正常显示,结合9可以看出,java中字符串无关编码方式
11、以iso8859-1先编码后解码。

看懂以上部分,基本就可以明白编码和解码到底是什么,即便不清楚到底不同编码方式有什么区别也无所谓。

3、文件上传

window下获取到的文件名字一般是GBK编码,因此最好将含有上传文件功能的jsp页面设置编码方式为GBK。

<%@ page language="java" import="java.util.*"pageEncoding="GBK"%>

设置文件上传的数量(这里设置最多三个文件);

<body>
    <center><form  action="/mobile/servlet/FileUpload" method="post" enctype="multipart/form-data" >
        <input type="file" name="file1"><br>
        <input type="file" name="file2"><br>
        <input type="file" name="file3"><br>
        <input type="submit" value="确认上传">
        <br><br>
    </form></center>
</body>

这里要注意,文件上传不同于一般的url传值,因此需要设置传输的方式,enctype=”multipart/form-data”表示直接将二进制流上传;method=”post”表单上传方式设置为post,get方式不可能将文件整个内容放在url后面。

在后台处理上传的文件,这里一般都会借助smartUpload的jar包,该包很好找到,下载后添加到build path,这里需要说一下,有时候build path之后系统仍然找不到该jar包,这时将jar包直接复制粘贴到web-INF下的lib文件夹即可。

SmartUpload smartUpload=new SmartUpload();
smartUpload.initialize(this.getServletConfig(), request, response);
try{
    smartUpload.upload();
    String path=this.getServletContext().getRealPath("/");
    (new java.io.File(path=path+"/files")).mkdirs();
    Log.d(path);
    smartUpload.save(path);         
}
catch(SmartUploadException e){
    Log.d(e.toString());
}

这样上传的文件就会自动备份到服务根目录下files文件夹下,记得之前页面一定要用GBK编码,否则文件保存后文件名会乱码。

4、文件下载

<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%@ page import="java.io.*" %>
<%@ page import="util.*" %>
<!-- 上传文件时,这里采用gbk编码,否则上传到服务器以后显示的文件名为乱码 -->

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'fileOperator.jsp' starting page</title>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/angular.js/1.3.9/angular.min.js"></script>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>

  <body>
    <center><form  action="/mobile/servlet/FileUpload" method="post" enctype="multipart/form-data" >
        <input type="file" name="file1"><br>
        <input type="file" name="file2"><br>
        <input type="file" name="file3"><br>
        <input type="submit" value="确认上传">
        <br><br>
    </form></center>
  </body>
  <br><br>
  <script type="text/javascript">
    var app=angular.module('myApp',[]);
    app.controller('myController',function($scope,$http){
        <%
            String path=this.getServletContext().getRealPath("/")+"/files";
            File file=new File(path);
            path="";
            if(file.exists()&&file.isDirectory()&&file.listFiles().length!=0){

                File[] files=file.listFiles();
                for(File f:files){
                    Calendar c=Calendar.getInstance();
                    c.setTimeInMillis(f.lastModified());
                    Date date=c.getTime();
                    path+="{name:'"+f.getName()+"',size:"+f.length()/1024+",time:'"+date.toString()+"'},";
                }

                if(!path.equals("")){
                    path=path.substring(0,path.length()-1);
                }
                Log.d("文件名:"+path);
            }else{
                Log.d("目录不存在或没有文件");
            }

        %>
        $scope.files=[<%=path %>];
    });
  </script>
  <div ng-app="myApp" ng-controller="myController">
  <form action="/mobile/servlet/FileDownload">
    <div ng-repeat="x in files|orderBy:'size'" ><input type="radio" name="selected" value="{{x.name}}">{{x.name+"&nbsp;&nbsp;&nbsp;"+x.size+"KB"+"&nbsp;&nbsp;"+x.time}}</div>
    <input type="submit" value="download">
  </form>
  </div>
</html>

先借助java代码获取文件夹下的文件,然后通过angularJs框架来实现各种动作,点击文件可下载。
在servlet中对下载请求做出处理:

String [] values=request.getParameterValues("selected");
ServletOutputStream out=response.getOutputStream();
if(values.length!=0){
    String s=values[0];
    s=new String(s.getBytes("iso8859-1"),"GBK");
    Log.d(s);
    //设置返回头Header,告诉浏览器这是个需要下载的文件。
    response.setHeader("content-disposition", "attachment;filename="+s);
    String path=this.getServletContext().getRealPath("/")+"files/"+s;
    Log.d(path);
    File file=new File(path);
    if(file.exists()){
        Log.d("文件大小"+file.length());                
        FileInputStream in = new FileInputStream(file);
        //创建输出流
        byte buffer[] = new byte[1024];
        int len = 0;
        //循环将输入流中的内容读取到缓冲区当中
        int length=0;
        while((len=in.read(buffer))>0){
        //输出缓冲区的内容到浏览器,实现文件下载
            out.write(buffer, 0, len);
            length+=len;
        }
        out.flush();
        out.close();
        Log.d("传输的文件大小:"+length+"");
    }
}

这里不要尝试通过BufferReader等方式读取文件内容,会导致文件损坏的。
这里写图片描述

暂时没有实现多个文件同时下载,因此将下面的按钮设置为了“radio”。
这里写图片描述
这样就可以实现文件的下载功能。

猜你喜欢

转载自blog.csdn.net/lovingning/article/details/50475393