首页 > 代码库 > Android实现模拟表单上传
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
Android实现模拟表单上传