不通过httpinputfile如何上传文件到服务器

不通过httpinputfile如何上传文件到服务器,第1张

你是想用不想用http协议实现?还是只是不用 inputfile,如果不用http协议,可以用第三方插件,activeMq,或者其他的如果http,可以用异步query的fileUpload,或者,通过java建立httpclient上传

分析一:

在研究Volley框架的源码中,发现它在HTTP请求的使用上比较有意思,在Android

23及以上版本,使用的是HttpURLConnection,而在Android

22及以下版本,使用的是HttpClient。我也比较好奇这么使用的原因,于是专门找到了一位Google的工程师写的一篇博客,文中对

HttpURLConnection和HttpClient进行了对比,下面我就给大家简要地翻译一下。

原文地址:http://android-developersblogspotcom/2011/09/androids-http-clientshtml

大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,而Android中主要提供了两种方式来进行HTTP操

作,HttpURLConnection和HttpClient。这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、

以及连接池等功能。

HttpClient:

DefaultHttpClient和它的兄弟AndroidHttpClient都是HttpClient具体的实现类,它们都拥有众多的API,而且实现比较稳定,bug数量也很少。

但同时也由于HttpClient的API数量过多,使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以目前Android团队在提升和优化HttpClient方面的工作态度并不积极。

HttpURLConnection:

HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序。虽然HttpURLConnection的API提供的比较简单,但是同时这也使得我们可以更加容易地去使用和扩展它。

不过在Android 22版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

[java] view plaincopy

private void disableConnectionReuseIfNecessary() {

// 这是一个22版本之前的bug

if (IntegerparseInt(BuildVERSIONSDK) < BuildVERSION_CODESFROYO) {

SystemsetProperty("httpkeepAlive", "false");

}

}

配置你的Web服务器来支持对客户端的响应进行压缩的功能,从而可以在这一改进上获取到最大的好处。如果在压缩响应的时候出现了问题,这篇文档会告诉你如何禁用掉这个功能。

是如果启动了响应压缩的功能,HTTP响应头里的Content-Length就会代表着压缩后的长度,这时再使用getContentLength()

方法来取出解压后的数据就是错误的了。正确的做法应该是一直调用InputStreamread()方法来读取响应数据,一直到出现-1为止。

们在Android 23版本中还增加了一些HTTPS方面的改进,现在HttpsURLConnection会使用SNI(Server Name

Indication)的方式进行连接,使得多个HTTPS主机可以共享同一个IP地址。除此之外,还增加了一些压缩和会话的机制。如果连接失败,它会自

动去尝试重新进行连接。这使得HttpsURLConnection可以在不破坏老版本兼容性的前提下,更加高效地连接最新的服务器。

在Android 40版本中,我们又添加了一些响应的缓存机制。当缓存被安装后(调用HttpResponseCache的install()方法),所有的HTTP请求都会满足以下三种情况:

所有的缓存响应都由本地存储来提供。因为没有必要去发起任务的网络连接请求,所有的响应都可以立刻获取到。

情况而定的缓存响应必须要有服务器来进行更新检查。比如说客户端发起了一条类似于 “如果/foopng这张发生了改变,就将它发送给我”

这样的请求,服务器需要将更新后的数据进行返回,或者返回一个304 Not

Modified状态。如果请求的内容没有发生,客户端就不会下载任何数据。

没有缓存的响应都是由服务器直接提供的。这部分响应会在稍后存储到响应缓存中。

由于这个功能是在40之后的版本才有的,通常我们就可以使用反射的方式来启动响应缓存功能。下面的示例代码展示了如何在Android 40及以后的版本中去启用响应缓存的功能,同时还不会影响到之前的版本:

[java] view plaincopy

private void enableHttpResponseCache() {

try {

long httpCacheSize = 10 1024 1024; // 10 MiB

File httpCacheDir = new File(getCacheDir(), "http");

ClassforName("androidnethttpHttpResponseCache")

getMethod("install", Fileclass, longclass)

invoke(null, httpCacheDir, httpCacheSize);

} catch (Exception httpResponseCacheNotAvailable) {

}

}

你也应该同时配置一下你的Web服务器,在HTTP响应上加入缓存的消息头。哪一种才是最好的?在Android

22版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。

而在Android

23版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可

以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在

以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。

分析二:

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP

协议来访问网络资源。在 JDK 的 javanet 包中已经提供了访问 HTTP 协议的基本功能:HttpURLConnection。

HttpURLConnection是java的标准类,HttpURLConnection继承自URLConnection,可用于向指定网站发送GET请求、POST请求。它在URLConnection的基础上提供了如下便捷的方法:

int getResponseCode():获取服务器的响应代码。

String getResponseMessage():获取服务器的响应消息。

String getResponseMethod():获取发送请求的方法。

void setRequestMethod(String method):设置发送请求的方法。

在一般情况下,如果只是需要Web站点的某个简单页面提交请求并获取服务器响应,HttpURLConnection完全可以胜任。但在绝大部分情

况下,Web站点的网页可能没这么简单,这些页面并不是通过一个简单的URL就可访问的,可能需要用户登录而且具有相应的权限才可访问该页面。在这种情况

下,就需要涉及Session、Cookie的处理了,如果打算使用HttpURLConnection来处理这些细节,当然也是可能实现的,只是处理起

来难度就大了。

为了更好地处理向Web站点请求,包括处理Session、Cookie等细节问题,Apache开源组织提供了一个HttpClient项目,看它的

名称就知道,它是一个简单的HTTP客户端(并不是浏览器),可以用于发送HTTP请求,接收HTTP响应。但不会缓存服务器的响应,不能执行HTML页

面中嵌入的Javascript代码;也不会对页面内容进行任何解析、处理。

简单来说,HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情

HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、

接收

响应,以及管理HTTP连接。

使用HttpClient发送请求、接收响应很简单,只要如下几步即可。

创建HttpClient对象

如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。

如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。

调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法返回一个HttpResponse。

用HttpResponse的getAllHeaders()、getHeaders(String

name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器

的响应内容。程序可通过该对象获取服务器的响应内容。

另外,Android已经成功地集成了HttpClient,这意味着开发人员可以直接在Android应用中使用Httpclient来访问提交请求、接收响应。

比如一个Android应用需要向指定页面发送请求,但该页面并不是一个简单的页面,只有当用户已经登录,而且登录用户的用户名有效时才可访问该页面。如果使用HttpURLConnection来访问这个被保护的页面,那么需要处理的细节就太复杂了。

其实访问Web应用中被保护的页面,使用浏览器则十分简单,用户通过系统提供的登录页面登录系统,浏览器会负责维护与服务器之间的Sesion,如果用户登录的用户名、密码符合要求,就可以访问被保护资源了。

在Android应用程序中,则可使用HttpClient来登录系统,只要应用程序使用同一个HttpClient发送请求,HttpClient会

自动维护与服务器之间的Session状态,也就是说程序第一次使用HttpClient登录系统后,接下来使用HttpClient即可访问被保护页而

了。

首先,需要明确一下http通信流程,Android目前提供两种http通信方式,HttpURLConnection和HttpClient,HttpURLConnection多用于发送或接收流式数据,因此比较适合上传/下载文件,HttpClient相对来讲更大更全能,但是速度相对也要慢一点。在此只介绍HttpClient的通信流程:

1创建HttpClient对象,改对象可以用来多次发送不同的http请求

2创建HttpPost或HttpGet对象,设置参数,每发送一次http请求,都需要这样一个对象

3利用HttpClient的execute方法发送请求并等待结果,该方法会一直阻塞当前线程,直到返回结果或抛出异常。

4针对结果和异常做相应处理

根据上述流程,发现在设计类的时候,有几点需要考虑到:

1HttpClient对象可以重复使用,因此可以作为类的静态变量

2HttpPost/HttpGet对象一般无法重复使用(如果你每次请求的参数都差不多,也可以重复使用),因此可以创建一个方法用来初始化,同时设置一些需要上传到服务器的资源

3目前Android不再支持在UI线程中发起Http请求,实际上也不该这么做,因为这样会阻塞UI线程。因此还需要一个子线程,用来发起Http请求,即执行execute方法

4不同的请求对应不同的返回结果,对于如何处理返回结果(一般来说都是解析json&更新UI),需要有一定的自由度。

5最简单的方法是,每次需要发送http请求时,开一个子线程用于发送请求,子线程中接收到结果或抛出异常时,根据情况给UI线程发送message,最后在UI线程的handler的handleMessage方法中做结果解析和UI更新。这么写虽然简单,但是UI线程和Http请求的耦合度很高,而且代码比较散乱、丑陋。

import javaioBufferedReader;

import javaioIOException;

import javaioInputStream;

import javaioInputStreamReader;

import javaioUnsupportedEncodingException;

import javanetHttpURLConnection;

import javanetMalformedURLException;

import javanetURL;

import javautilArrayList;

import javautilList;

import orgapachehttpHttpEntity;

import orgapachehttpHttpResponse;

import orgapachehttpHttpStatus;

import orgapachehttpNameValuePair;

import orgapachehttpclientClientProtocolException;

import orgapachehttpclientHttpClient;

import orgapachehttpcliententityUrlEncodedFormEntity;

import orgapachehttpclientmethodsHttpGet;

import orgapachehttpclientmethodsHttpPost;

import orgapachehttpimplclientDefaultHttpClient;

import orgapachehttpmessageBasicNameValuePair;

import orgapachehttputilEntityUtils;

import androidappActivity;

import androidosBundle;

import androidosHandler;

import androidosMessage;

import androidviewView;

import androidwidgetButton;

import androidwidgetTextView;

public class Test extends Activity implements Runnable{

/ Called when the activity is first created /

private Button btn_get = null;

private Button btn_post = null;

private TextView tv_rp = null;

@Override

public void onCreate(Bundle savedInstanceState) {

superonCreate(savedInstanceState);

setContentView(Rlayoutmain);

btn_get = (Button) thisfindViewById(RidButton01);

btn_post = (Button) thisfindViewById(RidButton02);

tv_rp = (TextView) thisfindViewById(RidTextView);

btn_getsetOnClickListener(new ButtonOnClickListener(){

public void onClick(View v) {

// TODO Auto-generated method stub

String httpUrl = "http://1921680132:8080/Android/httpreqjsppar=request-get";

HttpGet request = new HttpGet(httpUrl);

HttpClient httpClient = new DefaultHttpClient();

try {

HttpResponse response = httpClientexecute(request);

if(responsegetStatusLine()getStatusCode()==HttpStatusSC_OK){

String str = EntityUtilstoString(responsegetEntity());

tv_rpsetText(str);

}else{

tv_rpsetText("请求错误");

}

} catch (ClientProtocolException e) {

// TODO Auto-generated catch block

eprintStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

eprintStackTrace();

}

}

});

btn_postsetOnClickListener(new ButtonOnClickListener(){

public void onClick(View v) {

// TODO Auto-generated method stub

String httpUrl = "http://1921680132:8080/Android/httpreqjsp";

HttpPost request = new HttpPost(httpUrl);

List<namevaluepair> params = new ArrayList<namevaluepair>();

paramsadd(new BasicNameValuePair("par","request-post"));

try {

HttpEntity entity = new UrlEncodedFormEntity(params, "UTF-8");

requestsetEntity(entity);

HttpClient client = new DefaultHttpClient();

HttpResponse response = clientexecute(request);

if(responsegetStatusLine()getStatusCode()==HttpStatusSC_OK){

String str = EntityUtilstoString(responsegetEntity());

tv_rpsetText(str);

}else{

tv_rpsetText("请求错误");

}

} catch (UnsupportedEncodingException e) {

// TODO Auto-generated catch block

eprintStackTrace();

} catch (ClientProtocolException e) {

// TODO Auto-generated catch block

eprintStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

eprintStackTrace();

}

}

});

new Thread(this)start();

}

public void refresh(){

String httpUrl = "http://1921680132:8080/Android/httpreqjsp";

try {

URL url = new URL(httpUrl);

HttpURLConnection urlConn = (HttpURLConnection) urlopenConnection();

urlConnconnect();

InputStream input = urlConngetInputStream();

InputStreamReader inputreader = new InputStreamReader(input);

BufferedReader reader = new BufferedReader(inputreader);

String str = null;

StringBuffer sb = new StringBuffer();

while((str = readerreadLine())!= null){

sbappend(str)append("\n");

}

if(sb != null){

tv_rpsetText(sbtoString());

}else{

tv_rpsetText("NULL");

}

readerclose();

inputreaderclose();

inputclose();

reader = null;

inputreader = null;

input = null;

} catch (MalformedURLException e) {

eprintStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

eprintStackTrace();

}

}

public Handler handler = new Handler(){

public void handleMessage(Message msg){

superhandleMessage(msg);

refresh();

}

};

public void run() {

// TODO Auto-generated method stub

while(true){

try {

Threadsleep(1000);

handlersendMessage(handlerobtainMessage());

} catch (InterruptedException e) {

// TODO Auto-generated catch block

eprintStackTrace();

}

}

}

}

相信很多开发者会把存放到七牛上,我的web站点也是吧存储到七牛上,对于以为主的站点,这样可以节省很大带宽。

将上传到七牛服务器的重点就是获得上传凭证uploadToken,直接把AccessKey和Secret放到客户端太不安全,容易被反编译。所以需要在服务器端根据AccessKey和Secret动态生成一个uploadToken,然后传回到客户端,客户端通过这个uploadToken将上传到七牛服务器。

第一、在服务器端生成uploadToken

//将上传到七牛 start

$bucket='七牛空间名称';

$expires = 3600;

$accessKey='去七牛查看';

$secretKey='去七牛查看';

$client = new QiniuClient($accessKey,$secretKey);

$flags = array();

$scope = $bucket;

$deadline = time() + $expires;

$flags['scope'] = $scope;

$flags['deadline'] = $deadline;

$flags['returnBody'] = null;

echo $client->uploadToken($flags);

这里注意一下bucket:七牛空间名称和deadline:uploadToken失效时间,具体可查看一下官网上传凭证介绍

uploadToken($flags)是自己封装的用于生成上传凭证的函数

public function uploadToken($flags) { if(!isset($flags['deadline'])) $flags['deadline'] = 3600 + time(); $encodedFlags = self::urlsafe_base64_encode(json_encode($flags)); $sign = hash_hmac('sha1', $encodedFlags, $this->secretKey, true); $encodedSign = self::urlsafe_base64_encode($sign); $token = $this->accessKey':'$encodedSign ':' $encodedFlags; return $token; }

public static function urlsafe_base64_encode($str){

$find = array("+","/");

$replace = array("-", "_");

return str_replace($find, $replace, base64_encode($str));

}

第二、下载qiniu-android-sdk-700jar和android-async-http-146并导入项目

第三、android上传

由于Android40 以后不允许在主线程进行网络连接,所以需要新开个线程来获取上传凭证。

/

上传到七牛

/

private void uploadImg(){

new Thread(new Runnable(){

@Override

public void run() {

//获得七牛上传凭证uploadToken

String token=getUploadToken();

//手机SD卡存放路径

String imgPath="";

try {

imgPath=FileUtilgetBasePath()+ "/testjpg";

} catch (IOException e) {

eprintStackTrace();

}

if(token!=null){

String data = imgPath;

//名称为当前日期+随机数生成

String key = getRandomFileName();

UploadManager uploadManager = new UploadManager();

uploadManagerput(data, key, token,

new UpCompletionHandler() {

@Override

public void complete(String arg0, ResponseInfo info, JSONObject response) {

// TODO Auto-generated method stub

Logi("qiniu", infotoString());

}

}, null);

}

else{

Logi("fail", "上传失败");

}

}

})start();

}

FileUtilgetBasePath()使用来获取SD卡基本路径,getRandomFileName()生成一个随机数来命名上传,具体方法我在这就不写了。

获得上传凭证的方法也很简单,直接使用httpget和服务器通信,获得第一步中生成的数据即可。(注意10022是模拟器提供的特殊IP,等同于在电脑端的环回测试IP127001)

/

获得七牛上传凭证uploadtoken

/

private String getUploadToken()

{

HttpClient client = new DefaultHttpClient();

StringBuilder builder = new StringBuilder();

HttpGet myget = new HttpGet("");

try {

HttpResponse response = clientexecute(myget);

BufferedReader reader = new BufferedReader(new InputStreamReader(

responsegetEntity()getContent()));

for (String s = readerreadLine(); s != null; s = readerreadLine()) {

builderappend(s);

}

return buildertoString();

} catch (Exception e) {

Logi("url response", "false");

eprintStackTrace();

return null;

}

}

通过LOG日志可以看到Qiniu--success,说明上传成功。

Android应用开发中,会经常要提交数据到服务器和从服务器得到数据,本文主要是给出了利用http协议采用HttpClient方式向服务器提交数据的方法。

代码比较简单,这里不去过多的阐述,直接看代码。

/

@author Dylan 本类封装了Android中向web服务器提交数据的两种方式四种方法

/

public class SubmitDataByHttpClientAndOrdinaryWay {

使用get请求以普通方式提交数据

@param map

传递进来的数据,以map的形式进行了封装

@param path

要求服务器servlet的地址

@return 返回的boolean类型的参数

@throws Exception

/

public Boolean submitDataByDoGet(Map<String, String> map, String path)

throws Exception {

// 拼凑出请求地址

StringBuilder sb = new StringBuilder(path);

sbappend("");

for (MapEntry<String, String> entry : mapentrySet()) {

sbappend(entrygetKey())append("=")append(entrygetValue());

sbappend("&");

}

sbdeleteCharAt(sblength() - 1);

String str = sbtoString();

Systemoutprintln(str);

URL Url = new URL(str);

HttpURLConnection HttpConn = (HttpURLConnection) UrlopenConnection();

HttpConnsetRequestMethod("GET");

HttpConnsetReadTimeout(5000);

// GET方式的请求不用设置什么DoOutPut()之类的吗?

if (HttpConngetResponseCode() == HttpURLConnectionHTTP_OK) {

return true;

}

return false;

}

/

普通方式的DoPost请求提交数据

@param map

传递进来的数据,以map的形式进行了封装

@param path

要求服务器servlet的地址

@return 返回的boolean类型的参数

@throws Exception

/

public Boolean submitDataByDoPost(Map<String, String> map, String path) throws Exception {

// 注意Post地址中是不带参数的,所以newURL的时候要注意不能加上后面的参数

URL Url = new URL(path);

// Post方式提交的时候参数和URL是分开提交的,参数形式是这样子的:name=y&age=6

StringBuilder sb = new StringBuilder();

// sbappend("");

for (MapEntry<String, String> entry : mapentrySet()) {

sbappend(entrygetKey())append("=")append(entrygetValue());

sbappend("&");

}

sbdeleteCharAt(sblength() - 1);

String str = sbtoString();[/font][/size]

HttpURLConnection HttpConn = (HttpURLConnection) UrlopenConnection();

HttpConnsetRequestMethod("POST");

HttpConnsetReadTimeout(5000);

HttpConnsetDoOutput(true);

HttpConnsetRequestProperty("Content-Type",

"application/x-www-form-urlencoded");

HttpConnsetRequestProperty("Content-Length",

StringvalueOf(strgetBytes()length));

OutputStream os = HttpConngetOutputStream();

oswrite(strgetBytes());

if (HttpConngetResponseCode() == HttpURLConnectionHTTP_OK) {

return true;

}

return false;

}

/

以HttpClient的DoGet方式向服务器发送请数据

@param map

传递进来的数据,以map的形式进行了封装

@param path

要求服务器servlet的地址

@return 返回的boolean类型的参数

@throws Exception

/

public Boolean submitDataByHttpClientDoGet(Map<String, String> map,

String path) throws Exception {

HttpClient hc = new DefaultHttpClient();

// 请求路径

StringBuilder sb = new StringBuilder(path);

sbappend("");

for (MapEntry<String, String> entry : mapentrySet()) {

sbappend(entrygetKey())append("=")append(entrygetValue());

sbappend("&");

}

sbdeleteCharAt(sblength() - 1);

String str = sbtoString();

Systemoutprintln(str);

HttpGet request = new HttpGet(sbtoString());[/font][/size]

HttpResponse response = hcexecute(request);

if (responsegetStatusLine()getStatusCode() == HttpURLConnectionHTTP_OK) {

return true;

}

return false;

}

复制代码

/

以HttpClient的DoPost方式提交数据到服务器

@param map

传递进来的数据,以map的形式进行了封装

@param path

要求服务器servlet的地址

@return 返回的boolean类型的参数

@throws Exception

/

public Boolean submintDataByHttpClientDoPost(Map<String, String> map, String path) throws Exception {

// 1 获得一个相当于浏览器对象HttpClient,使用这个接口的实现类来创建对象,DefaultHttpClient

HttpClient hc = new DefaultHttpClient();

// DoPost方式请求的时候设置请求,关键是路径

HttpPost request = new HttpPost(path);

// 2 为请求设置请求参数,也即是将要上传到web服务器上的参数

List<NameValuePair> parameters = new ArrayList<NameValuePair>();

for (MapEntry<String, String> entry : mapentrySet()) {

NameValuePair nameValuePairs = new BasicNameValuePair(

entrygetKey(), entrygetValue());

parametersadd(nameValuePairs);

}

// 请求实体HttpEntity也是一个接口,我们用它的实现类UrlEncodedFormEntity来创建对象,注意后面一个String类型的参数是用来指定编码的

HttpEntity entity = new UrlEncodedFormEntity(parameters, "UTF-8");

requestsetEntity(entity);

// 3 执行请求

HttpResponse response = hcexecute(request);

// 4 通过返回码来判断请求成功与否

if (responsegetStatusLine()getStatusCode() == HttpURLConnectionHTTP_OK) {

return true;

}

return false;

}

}

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 不通过httpinputfile如何上传文件到服务器

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情