zoukankan      html  css  js  c++  java
  • Android实现模拟表单上传

    很久以前,写过一篇关于下载的文章:基于HTTP协议的下载功能实现,今天对于Android上的文件上传,也简单的提两笔。在Android上,一般使用Http 模拟表单或者FTP来进行文件上传,使用FTP协议,可以直接使用Appache的FTPClient,使用方法很简单,不再赘述。这里主要说明一下Http模拟表单上传的实现。

    模拟表单上传,其实也很简单,主要需要在Http post 的数据体中构建表单信息(multipart/form),表单数据格式的规范,可以参考REC标准。下面是一个格式示例:

           ...
    Content-Type: multipart/form-data; boundary=
    ------WebKitFormBoundaryK7Ck1eEROPVUf1De
           Content-Length: 145000
    ...
    ------WebKitFormBoundaryK7Ck1eEROPVUf1De Content-Disposition: form-data; name="fileKey"; filename="bg_entry.png" Content-Type: image/png DATA OF FILE
           ------WebKitFormBoundaryK7Ck1eEROPVUf1De--
    
    

    表单请求重点在两部分:

    Header

    1.通过Content-Type告知Server这是一个表单提交请求,并声明自己使用的Boundary。Boundary相当于一个分隔符,用于标志表单数据的开始和结束。

    2.通过Content-Length告诉本次请求的数据长度,Post Body的长度(包括上传文件长度)。

    Body:

    1.以Boundary分割表单数据。

    2.表单参数相当于简单的Header,一般包括Content-Disposition(文件信息)和Content-Type(数据类型)两个字段。

    3.各部分、各字段之间都要以CRLF分割。

    4.最后以Boundary加上“--”结束表单请求。

    核心代码如下:

        protected String doUpload(HttpURLConnection connection, UploadParam param) throws Exception {
            String path = param.getPath();
            String fileKey = TextUtils.isEmpty(param.getFileKey()) ? "file" : param.getFileKey();
            String fileName = param.getFileName();
            String fileType = TextUtils.isEmpty(param.getContentType()) ? MIME_TYPE_ALL : param.getContentType();
    
            DataOutputStream outs = null;
            BufferedReader ins = null;
            FileInputStream fouts = null;
            String response = null;
            try {
                //    Content-Disposition: form-data; name="fileKey"; filename="bg_entry.png"
               //            Content-Type: image/png
                StringBuilder builder = new StringBuilder(buildParams(param.getParams()));
                builder.append(getBoundaryPrefixed())
                        .append(CRLF)
                        .append(String.format(HEADER_CONTENT_DISPOSITION + COLON_SPACE + FORM_DATA + SEMICOLON_SPACE + FILENAME, fileKey, fileName))
                        .append(CRLF)
                        .append(HEADER_CONTENT_TYPE).append(fileType)
                        .append(CRLF)
                        //Must jump to new line to indicate the beginning of data.
                        .append(CRLF);
                byte[] headBuf = builder.toString().getBytes(CHARSET_UTF8);
                //Must jump to new line to indicate the end of data.
                byte[] tailBuf = (CRLF + getBoundaryPrefixed() + BOUNDARY_PREFIX + CRLF).getBytes(CHARSET_UTF8);
                long currentBytes = 0;
                File file = new File(path);
                long totalSize = file.length() + headBuf.length + tailBuf.length;
                //Generally speaking,Files larger than 4M should use streaming mode.
                if (totalSize > 4 * 1024 * 1024) {
                    //Avoid oom when post large file.Ether way is ok.
                    connection.setChunkedStreamingMode(1024);
    //                connection.setFixedLengthStreamingMode(totalSize);
                }
                connection.setRequestProperty(HEADER_CONTENT_LENGTH, String.valueOf(totalSize));
                connection.connect();
    
                outs = new DataOutputStream(connection.getOutputStream());
                outs.write(headBuf);
                currentBytes += headBuf.length;
                updateProgress(currentBytes, totalSize);
                fouts = new FileInputStream(file);
                byte[] buffer = new byte[1024];
                int length = -1;
                long startTime = System.currentTimeMillis();
                long now = 0;
                while ((length = fouts.read(buffer)) != -1) {
                    if (length > 0) {
                        outs.write(buffer, 0, length);
                        currentBytes += length;
                        now = System.currentTimeMillis();
                        if (now - startTime >= PROGRESS_RATE) {
                            updateProgress(currentBytes, totalSize);
                            startTime = now;
                        }
                    }
                    if (!canRun()) {
                        throw new Exception("Upload cancelled");
                    }
                }
                outs.write(tailBuf);
                outs.flush();
                updateProgress(totalSize, totalSize);
    
                fouts.close();
                fouts = null;
    
                //Response.
                if (connection.getResponseCode() != 200) {
                    throw new IllegalStateException(String.format("Error upload response: code:%s  msg:%s", connection.getResponseCode(), connection.getResponseMessage()));
                }
                ins = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                StringBuffer b = new StringBuffer();
                while ((line = ins.readLine()) != null) {
                    b.append(line);
                    if (!canRun()) {
                        throw new Exception("Upload cancelled");
                    }
                }
    
                response = b.toString();
                if (TextUtils.isEmpty(response)) {
                    throw new NullPointerException("Null response: " + response);
                }
                outs.close();
                outs = null;
                ins.close();
                ins = null;
            } finally {
                if (fouts != null) {
                    fouts.close();
                    fouts = null;
                }
                if (outs != null) {
                    outs.close();
                    outs = null;
                }
                if (ins != null) {
                    ins.close();
                    ins = null;
                }
            }
            return response;
        }

    主要步凑为:

    1.配置Header参数

    2.构建表单参数

    3.读取和发送文件内容

    4.获取响应码

    其中值得注意的是,一般情况下,上传会把所有的文件内容读取到内存中再统一发送,如果文件过大,将可能导致内存溢出。所以在判断文件内容大于4MB时,使用Chunked模式或Stream模式来避免OOM。

                if (totalSize > 4 * 1024 * 1024) {
                    //Avoid oom when post large file.Ether way is ok.
                    connection.setChunkedStreamingMode(1024);
                    //connection.setFixedLengthStreamingMode(totalSize);
                }

    更多代码详情请参考:TransferLibrary——一个Android文件传输库,主要实现基于Http的文件上传和下载,简单方便,支持多任务下载,断点续传等等,欢迎小伙伴们使用交流:D

  • 相关阅读:
    mongodb 添加用户
    mongo 安装
    python 操作redis
    python 安装 redis
    redis 命令文档网址
    redis 事务
    Redis key命令
    手动卸载的vs2010
    个人封装JavaScript函数
    女学-温砚如老师的人生女学
  • 原文地址:https://www.cnblogs.com/oxgen/p/7189854.html
Copyright © 2011-2022 走看看