編輯:關於Android編程
最近項目中需要客戶端往服務器傳輸圖片,並且還需要附帶一些普通參數,研究了幾天,把結果記錄下。
首先客戶端可服務端進行通信一般都是有http請求來發送和接收數據,這裡android中有兩種HttpClient和HttpURLConnection,這兩個都可以和後台服務器進行網絡通信,但是如何選擇哪個來進行文件傳輸呢?
谷歌官方是提倡我們使用HttpURLConnection來和服務器進行通信(這個是在android.jar中就有),HttpClient這個是org.apache.http.client.HttpClient下的一個工具類,根據網上的一些測試,得出的一些理論結果是使用HttpURLConnection來傳輸文件比HttpClient要快一些,在普通參數傳輸上都差不多。
既然谷歌推薦,所以我麼這裡就選擇HttpURLConnection來實現傳輸文件。
效果圖非常簡單
Android客戶端邏輯:
1: 點擊選圖片—>打開系統的相冊選擇;
2:點擊上傳圖片,把圖片上傳到服務器,然後顯示進度,以及上傳完成後顯示服務器返回的json數據。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCgk8cD7K18/Iv7S/tM/uxL+1xL3hubk8YnIgLz4NCgk8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20150421/2015042108403722.png" title="\" />
- 顯示的界面在MainAcvtitvity中;
- ConfigParamters是http請求的公共參數配置;
- HttpUrlConn是http請求主要類;
- Uploadllinsener用來監聽文件上傳的進度,以及上傳的狀態(成功,失敗,進度)。這裡主要是看看HttpUrlConn請求類:
public class HttpUrlConn {
//標簽
private String TAG=HttpUrlConn;
//HttpURLConnection請求
private HttpURLConnection myhttpconnect;
//結束點
private String end =
;
//分割標記twoHyphens
private String twoHyphens = --;
//分割標記
private String boundary = *****;
//數據輸出管道
private DataOutputStream ds=null;
//文件上傳監聽
private UploadLinsener uploadlinsener;
//文件總大小
private double allFilesize=0;
//當前傳輸了多少
private double curr_ret=0;
/**
* HttpURLConnection連接服務器請求
* @param path :接口地址
* @param param1 :普通參數
* @param paramFiles:需要上傳的文件參數
* @param config:連接公共屬性配置
* @return
*/
public HttpURLConnection getHttpUrlConnInstances(String path,
HashMap paramString,
HashMap paramFiles,
ConfigParamters config){
try {
//這裡可以放普通參數,(第一種方法)
// StringBuffer sb=new StringBuffer(path);
// if(null!=paramString&¶mString.size()>0){
// sb.append(?);
// for(Entry data:paramString.entrySet()){
// sb.append(data.getKey()+=+data.getValue());
// sb.append(&);
// }
// }
// path=sb.substring(0, sb.lastIndexOf(&));
URL url=new URL(path);
myhttpconnect=(HttpURLConnection)
url.openConnection();
/* 允許Input、Output,post中不使用Cache */
myhttpconnect.setDoInput(config.isDoInput());
myhttpconnect.setDoOutput(config.isDoOutput());
myhttpconnect.setUseCaches(config.isUseCaches());
/* 設置傳送的method=POST */
myhttpconnect.
setRequestMethod(config.getUseMethod());
/*設置屬性 setRequestProperty */
myhttpconnect
.setRequestProperty(Connection, Keep-Alive);
myhttpconnect.setRequestProperty(Charset, UTF-8);
myhttpconnect.setRequestProperty(Content-Type, multipart/form-data;boundary= + boundary);
/* 設置DataOutputStream */
ds = new DataOutputStream(
myhttpconnect.getOutputStream());
/*把普通參數寫到服務器*/
if(null!=paramString&¶mString.size()>0){
writeStringParam(ds, paramString);
}
/*把文件參數寫到服務器*/
if(null!=paramFiles&¶mFiles.size()>0){
writeFileParam(ds, paramFiles);
}
//添加結尾標志
paramsEnd(ds);
if(myhttpconnect.getResponseCode()
==HttpURLConnection.HTTP_OK){
InputStream is =
myhttpconnect.getInputStream();
String successMsg=InputStremtoString(is);
//成功
if(uploadlinsener!=null){
uploadlinsener.uploadSucsess(successMsg);
}
}else{
uploadlinsener.uploadFail(錯誤,錯誤碼
ResponseCode:+
myhttpconnect.getResponseCode());
}
ds.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(uploadlinsener!=null){
uploadlinsener.uploadFail(e.toString());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(uploadlinsener!=null){
uploadlinsener.uploadFail(e.toString());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(uploadlinsener!=null){
uploadlinsener.uploadFail(e.toString());
}
}
return myhttpconnect;
}
/**
* 把字節輸入流轉換成字符串
* @return
*/
private String InputStremtoString(InputStream is)throws Exception{
int ch;
StringBuffer b = new StringBuffer();
byte[] bt=new byte[1024];
while ((ch = is.read(bt)) != -1)
{
b.append(new String(bt, 0, ch, UTF-8));
}
return b.toString();
}
/**
* 傳普通參數(第二種方法)
* @param parm:參數
*/
private void writeStringParam(DataOutputStream ds,
HashMap params){
try{
Set keySet = params.keySet();
for (Iterator it =keySet.iterator();
it.hasNext();){
String name = it.next();
String value = params.get(name);
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes(Content-Disposition: form-data; name= + name ++ end);
ds.writeBytes(end);
//URLEncoder.encode(value)
ds.writeBytes(new String(value.getBytes(),UTF-8)+ end);
}
}catch(Exception e){
e.printStackTrace();
}
}
/***
* 傳輸文件
* @param ds :數據傳輸通道
* @param params :參數
*/
private void writeFileParam(DataOutputStream ds,HashMap params){
try{
Set keySet = params.keySet();
getFilesSize(params);
for (Iterator it =keySet.iterator();
it.hasNext();){
String name = it.next();
File value = params.get(name);
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes(Content-Disposition: form-data; name= + name + ; filename= + URLEncoder.encode(value.getName()) + +end);
ds.writeBytes(Content-Type: + getContentType(value) + end);
ds.writeBytes(end);
ds.write(getBytes(value));
ds.writeBytes(end);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 獲取要傳輸文件總共大小
* @param params
*/
private void getFilesSize(HashMap params){
for(Entry data:params.entrySet()){
File f=data.getValue();
allFilesize+=f.length();
}
Log.i(TAG, AllFileSize:+allFilesize);
}
/**
* 獲取文件的上傳類型,圖片格式為image/png,image/jpg等。非圖片為application/octet-stream
* @param f 文件
* @return
* @throws Exception
*/
private String getContentType(File f) throws Exception{
return application/octet-stream;
}
/**
* 添加結尾數據
* @param ds:數據傳輸通道
* * @throws Exception
*/
private void paramsEnd( DataOutputStream ds) throws Exception {
ds.writeBytes(twoHyphens + boundary +
twoHyphens + end);
ds.writeBytes(end);
}
/**
* 把文件轉換成字節數組
* @param f:文件
* @return
* @throws Exception
*/
private byte[] getBytes(File f) throws Exception{
FileInputStream in = new FileInputStream(f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = in.read(b)) != -1)
{
out.write(b, 0, n);
if(allFilesize>0){
curr_ret+=n;
double level=(curr_ret/allFilesize)*100;
Log.i(result, +level);
if(uploadlinsener!=null){
uploadlinsener.uploading(level);
}
}
}
in.close();
return out.toByteArray();
}
public void setUploadlinsener(UploadLinsener uploadlinsener) {
this.uploadlinsener = uploadlinsener;
}
}
上面的代碼是HttpUrlConn的全部代碼。
1: getHttpUrlConnInstances()獲取並且請求連接服務器;
-方法中主要是獲取連接,並且配置連接,然後拼接參數,發送到服務器,然後通過myhttpconnect.getInputStream()
獲取到服務器返回的數據。
2:InputStremtoString(InputStream)把字節流轉換成String;
- 這裡主要是為了處理服務器返回的數據。(這裡注意編碼)
3:writeStringParam(DataOutputStream,HashMap
這個方法主要是為了傳輸普通的參數;)
- 把普通參數裝入HashMap
中然後循環寫出數據,這裡的數據是需要拼接的,因為我們提交數據是模擬from表單提交。
4:writeFileParam(DataOutputStream,HashMap
這個方法只要是用來傳輸文件參數;)
- 把需要傳輸的文件都裝在HashMap
中,循環遍歷hashmap中的文件,一一寫到服務器。(在這個方法中獲取了所有文件的總大小,用於計算當前的傳輸進度,還有這裡的name和服務器的要一樣。)
5:getFilesSize(HashMap
獲取文件的總大小;)
6:getContentType(File f)
獲取文件的上傳類型,圖片格式為image/png,image/jpg等。非圖片為application/octet-stream;
7:paramsEnd(DataOutputStream)
添加結尾數據;
8:getBytes(File)
把文件轉換成字節數組;
9:setUploadlinsener(UploadLinsener)
設置傳輸監聽;
這裡我們使用struts2的一個組件來實現配合客戶端文件上傳。
用struts,spring,hibernate三大框架搭建好的項目,這裡項目搭建過程就不演示了。
這裡的核心代碼在FileUploadAction中:
public class FileUploadAction extends ActionSupport{
public String phoneinfo=NXJ570;
public void uploadfielandparam() throws Exception{
System.out.println(upload);
HttpServletRequest request=
ServletActionContext.getRequest();
System.out.println(request.getContentType()); System.out.println(request.getCharacterEncoding());
System.out.println(request.getContentLength());
//臨時文件夾,在tomcat項目下的temp
String temp = ServletActionContext
.getServletContext()
.getRealPath()+ \temp;
System.out.println(temp);
//上傳文件存放地方
String basepath=ServletActionContext
.getServletContext()
.getRealPath()+\myfile;
//獲取Response
HttpServletResponse resp=
ServletActionContext.getResponse();
DiskFileItemFactory factory =
new DiskFileItemFactory();
//單個文件緩存最大值
factory.setSizeThreshold(10 * 1024 * 1024);
// 緩沖區
factory.setRepository(new File(temp));
//文件上傳組件(關鍵)
ServletFileUpload upload1=
new ServletFileUpload(factory);
upload1.setFileSizeMax(10000000);
upload1.setSizeMax(480000000);
upload1.setHeaderEncoding(UTF-8);
// 設置一旦文件大小超過getSizeThreshold()
//的值時數據存放在硬盤的目錄
// 開始讀取上傳信息
int index = 0;
List fileItems= null;
try
{
fileItems=upload1.parseRequest(request);
System.out.println(list= + fileItems);
}
catch (Exception e)
{
e.printStackTrace();
}
Iterator iter = fileItems.iterator();
// 依次處理每個上傳的文件
while (iter.hasNext())
{
FileItem item = (FileItem) iter.next();
// 忽略其他不是文件域的所有表單信息
if (!item.isFormField())
{
String name = item.getName();
// 獲取上傳文件名,包括路徑
name = name.substring(
name.lastIndexOf(\) + 1);
// 從全路徑中提取文件名
long size = item.getSize();
if ((name == null
|| name.equals()) && size == 0)
continue;
int point = name.indexOf(.);
name = (new Date()).getTime()
+ name.substring(point, name.length());
System.out.println(fileName=+name);
index++;
File fNew = new File(basepath, name);
try
{
item.write(fNew);
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
// 取出不是文件域的所有表單信息
{
//如果包含中文應寫為:(轉為UTF-8編碼)
String key=item.getFieldName();
String fieldvalue =
.getBytes(),UTF-8);//UTF-8
System.out
.println(
params:+key+=+fieldvalue);
}
}
//把對象轉換成json
Gson gson = new Gson();
myobj obj1=new myobj(0, seccessed,
服務器已經接收到文件);
String json = gson.toJson(obj1);
System.out.println(json);
//設置格式(必須設置,否則客戶端接收到的json中文亂碼)
resp.setContentType(text/html; charset=utf-8);
PrintWriter out = resp.getWriter();
out.println(json);
out.flush();
}
//實體對象
class myobj{
private String retcode;
private String retmessage;
private String retdata;
public myobj(String retcode,
String retmessage, String retdata) {
super();
this.retcode = retcode;
this.retmessage = retmessage;
this.retdata = retdata;
}
public String getRetcode() {
return retcode;
}
public void setRetcode(String retcode) {
this.retcode = retcode;
}
public String getRetmessage() {
return retmessage;
}
public void setRetmessage(String retmessage) {
this.retmessage = retmessage;
}
public String getRetdata() {
return retdata;
}
public void setRetdata(String retdata) {
this.retdata = retdata;
}
}
這個類是核心業務類,當客戶端發送請求是進入到這個方法中,並且獲取到參數數據。
在服務端有兩種方式能後獲取到android的普通參數,
一是通過HttpServletRequest的getParameter(arg0)方法來獲取;
二是從提交的表單中獲取:
//獲取key
String key=item.getFieldName();
//獲取vaule
String fieldvalue =new
String(item.getString().getBytes(),UTF-8);
經過測試傳輸速度的確挺快的5MB的圖片幾秒鐘就能傳輸完成,注意這裡如果是在android客戶端模擬機中調試程序,那裡的請求地址中的ip應該為10.0.0.2:8080…..否則找不到服務器,如果是真機上測試可以直接發布到你電腦的tomcat中,然後把ip改成你點擊的ip就可以了(這裡電腦和android手機需要連接到同一個網絡wifi,也就是內網,當然也可以拿到公司的服務器上測試)。
項目測試通過,效果也挺好的,不過這裡還有一個問題沒解決,就是通過from表單提交上來的普通參數如果是中文這裡好像還是會亂碼。
android原生操作json數據主要是兩個類 JSONObject 操作對象 JONSArray操作json數組對象轉j
手機燒屏是什麼意思?解決手機燒屏方法。手機燒屏怎麼辦?大家多數機友對於“手機燒屏”還不是很了解,很多人都不知道平時要注意什麼,才能避
SparseArray 目前有很多地方從性能優化方說使用SparseArray來替換hashMap,來節省內存,提高性能。Linkify.addLinks() 這個類可以
百度google大家多說的是任務管理器 kill掉adb 或者重啟adb server,但我任務管理器就沒有adb ,猜測是某個程序占用了adb端口。於是按此思路查找。5