很久以前,写过一篇关于下载的文章:基于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实现模拟表单上传的更多相关文章

  1. java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例

    java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例HttpClient 测试类,提供get post方法实例 package com.zdz.httpclient; i ...

  2. Netty学习笔记(一):接收nodejs模拟表单上传的文件

    好久不写博客了,也好久不写代码了,这两天临时遇上一个事情,觉得不难,加上觉得手有些生,就动手做了一下,结果遇上了不少坑,有新坑,有老坑,痛苦无比,现在总算差不多了,赶紧记录下来,希望以后不再重复这种痛 ...

  3. php Socket模拟表单上传文件函数_学习

    模拟上传文件的php代码 里面访问地址.主机.上传文件名.内容.分隔符可以修改   function postFile($file) {     $clf = "\r\n";   ...

  4. HttpClient(五)-- 模拟表单上传文件

    1.maven依赖 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId ...

  5. 使用CURL模拟表单上传文件

    //以下代码适合PHP7.x PHP5.6$file = new CURLFile('./127.zip','application/octet-stream');$file->setMimeT ...

  6. PHP CURL 模拟form表单上传遇到的小坑

    1:引用的时候 $parans ['img']=new \CURLFile($param); 传入的文件 在PHP版本5.5以上记得new CURLFile 不然会上传不成功 /** * http p ...

  7. 一般处理程序上传文件(html表单上传、aspx页面上传)

    html 表单上传文件        一般处理程序由于没有 apsx 页面的整个模型和控件的创建周期,而比较有效率.这里写一个用 html 表单进行文件上传的示例.        1. 表单元素选用 ...

  8. 相册选择头像或者拍照 上传头像以NSData 图片二进制格式 表单上传

    一.点击头像图片 或者按钮 在相册选择照片返回img,网络上传头像要用data表单上传 (1)上传头像属性 // 图片二进制格式 表单上传 @property (nonatomic, strong) ...

  9. [转]html5表单上传控件Files API

    表单上传控件:<input type="file" />(IE9及以下不支持下面这些功能,其它浏览器最新版本均已支持.) 1.允许上传文件数量 允许选择多个文件:< ...

随机推荐

  1. 你知道“移动端车牌识别”可以嵌入到PDA中应用吗?

    一.移动端车牌识别产品描述 移动端车牌识别软件是基于移动平台的OCR识别应用程序,支持Android/IOS等多种主流移动操作系统.该产品只需通过智能手机或Pad的摄像头对准车牌,无需拍照,实现自动采 ...

  2. 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率

    隐马尔科夫模型HMM(一)HMM模型 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数(TODO) 隐马尔科夫模型HMM(四)维特比算法 ...

  3. Python+requests库 POST接口图片上传

    捕获到POST接口的请求参数,如下: 包括data.file两个部分,上传代码如下: 比较简单,直接发送files参数即可 heads中 content-type 折腾了1个小时,呵呵

  4. 简化布隆过滤器——BitMap

    简化布隆过滤器--BitMap 前言 前段开发项目试就发现,一部分的代码实现存在着一些性能上的隐患.但当时忙于赶进度和由于卡发中的不稳定因素,想了许多解决方案也没有机会实施.最近,正好趁个机会进行一系 ...

  5. 每篇半小时1天入门MongoDB——3.MongoDB可视化及shell详解

    本篇主要介绍MongoDB可视化操作以及shell使用及命令,备份恢复.数据导入导出. MongoVUE安装和简单使用 使用mongo.exe 管理数据库虽然可行,功能也挺强大,但每次都要敲命令,即繁 ...

  6. jquery实现导航栏效果

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

  7. Web压力测试软件webbench

    官方网站:http://home.tiscali.cz/~cz210552/webbench.html下载地址:http://home.tiscali.cz/~cz210552/distfiles/w ...

  8. TypeScript技巧集锦(陆续更新)

    在C++项目中编译TypeScript(以下简称ts) 编辑ts文件的属性,项类型选择"自定义生产工具". 命令行输入tsc所在位置与编译参数,我的是"C:\Progra ...

  9. STM32CubeMX GPIO的使用

    一.GPIO口配置1.GPIO的主要配置有输入和输出① 作为普通GPIO输入:根据需要配置该引脚为浮空输入.带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块.② 作为普通GPIO输 ...

  10. WPF中实现类智能感知

    首先要做的事情就是定义一个popup来显示我们需要展示的东西 <Popup x:Name=" StaysOpen="False" Placement="B ...