昨天说老师说了android的网络存储,对协议有了一个更深的理解。
数据上传到网络,尽管 b/s 和c/s的架构不一样,但是它们还是能上传数据到网络,因为都遵循协议的规范,c/s存储数据到网络,思想模仿浏览器发送数据服务器的请求头信息,在底层通过android提供的socket去链接网络,发送相应的数据,如果协议没拼接错误的话,能获得正确的相应码,并作出相应的操作。
总的来说,其实android网络存储重点还是对协议的拼接的,在此过程中必须静下心来才能做好拼接操作。
b/s通过访问服务器的网络段的请求头数据如下:·
POST /ownUploadServer/doupload.action HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost/ownUploadServer/
Cookie: JSESSIONID=F0EE9F4C17CB90FCADB60AC34B6AB871; CNZZDATA1000112112=1371504373-1430749960-%7C1430817053;
Hm_lvt_b24307b87f33ce3e55789d58670dbbd5=1430809410; Hm_lvt_21a80e96f6b0bb699ad5f04db22194a4=1430809411; bdshare_firstime=1431935932462
Connection: keep-alive
通过观察得知,请求头信息比较重要的是:
POST /ownUploadServer/doupload.action HTTP/1.1 ,它包括了请求的方式,访问的资源地址,协议的版本。
Host: localhost ip信息
Connection: keep-alive 保持连接
传过去的参数:
-----------------------------28332323972849
Content-Disposition: form-data; name="picdesc"
12
-----------------------------28332323972849
Content-Disposition: form-data; name="size"
23
-----------------------------28332323972849
Content-Disposition: form-data; name="ptype" 人æ
-----------------------------28332323972849
Content-Disposition: form-data; name="pic1"; filename="2.png"
Content-Type: image/png
xxxx
-----------------------------28332323972849--
这里要注意的是协议的拼接非常的严格,一个地方错都会导致程序的崩溃的。
服务器端使用tomcat,客户端是addroid平台,下面实现数据存储:
客户端代码:上传的bean类
public class UploadFile implements Serializable{
private static final long serialVersionUID = 3445579255260832749L;
/**
*文件的内容
*/
private byte[] data;
/**
* 可以通过这个流读数据
*/
private InputStream inStream;
private File file;
/**
* 文件名
*/
private String filename;
/**
* 文件类型
*/
private String contentType="application/octet-stream";
private String parameterName;
public UploadFile(byte[] data, String filename, String contentType,
String parameterName) {
super();
this.data = data;
this.filename = filename;
this.contentType = contentType;
this.parameterName = parameterName;
}
public UploadFile(File file, String filename, String contentType,
String parameterName) {
super();
this.file = file;
this.filename = filename;
this.contentType = contentType;
this.parameterName = parameterName;
try {
this.inStream=new FileInputStream(this.file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public InputStream getInStream() {
return inStream;
}
public void setInStream(InputStream inStream) {
this.inStream = inStream;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getParameterName() {
return parameterName;
}
public void setParameterName(String parameterName) {
this.parameterName = parameterName;
}
}
上传帮助类:
package com.yc.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import com.yc.bean.UploadFile;;
public class WebUtil {
/**
* 文件上传
* @param address
* @param params
* @param uploadFiles
* @throws IOException
* @throws UnknownHostException
*/
public String postFileToServer(String address,Map params,List uploadFiles) throws UnknownHostException, IOException{
final String BOUNDARY="---------------------------1123240313494";
final String SEPARATOR="--"+BOUNDARY;
final String BREAKLINE="\r\n";
final String ENDLINE=BREAKLINE+SEPARATOR+"--";
long totalLength=0;
long fileTotalLength=0;
//计算uploadFiles部分的总长度
if(uploadFiles!=null&&uploadFiles.size()>0){
for(UploadFile uf:uploadFiles){
StringBuffer sb=new StringBuffer();
sb.append(SEPARATOR);
sb.append(BREAKLINE);
sb.append("Content-Disposition: form-data; name=\""+uf.getParameterName()+"\"; filename=\""+ uf.getFilename()+"\"");
sb.append(BREAKLINE);
sb.append("Content-Type: "+uf.getContentType());
sb.append(BREAKLINE).append(BREAKLINE);
//因为图片数据的后面也要加入一个\r\n
sb.append(BREAKLINE);
fileTotalLength+=sb.toString().getBytes().length;
//计算这个uf的真实长度
if(uf.getInStream()==null){
fileTotalLength+=uf.getData().length;
}else{
fileTotalLength+=uf.getFile().length();
}
}
}
//计算文本参数部分的总长度
StringBuffer textEntity=new StringBuffer();
if(params!=null&¶ms.size()>0){
for(Map.Entry entry:params.entrySet()){
textEntity.append(SEPARATOR);
textEntity.append(BREAKLINE);
textEntity.append("Content-Disposition: form-data; name=\""+entry.getKey()+"\"");
textEntity.append(BREAKLINE);
textEntity.append(BREAKLINE);
textEntity.append(entry.getValue());
textEntity.append(BREAKLINE);
}
}
//总长度
totalLength=fileTotalLength+textEntity.toString().getBytes().length+ENDLINE.getBytes().length;
//访问网络,发送协议
URL url=new URL(address);
//取出端口
int port=url.getPort()==-1?80:url.getPort();
//创建socket,以socket套接字访问服务器
Socket s=new Socket(InetAddress.getByName(url.getHost()),port);
//取出输入流
OutputStream oos=s.getOutputStream();
//拼接请求头
String requestCommand="POST "+url.getPath()+" HTTP/1.1"+BREAKLINE;
oos.write(requestCommand.getBytes());
String hostword="Host: "+url.getHost()+BREAKLINE;
oos.write(hostword.getBytes());
String connectionword="Connection: keep-alive"+BREAKLINE;
oos.write(connectionword.getBytes());
String contenttypeword="Content-Type: multipart/form-data; boundary="+BOUNDARY+BREAKLINE;
oos.write(contenttypeword.getBytes());
String contentlength="Content-Length: "+totalLength+BREAKLINE;
oos.write(contentlength.getBytes());
//请求头与请求实体间的空行
oos.write(BREAKLINE.getBytes());
//发出文本实体
oos.write(textEntity.toString().getBytes());
//发出文件
//计算出uploadFiles部分的总长度
if(uploadFiles!=null&&uploadFiles.size()>0){
for(UploadFile uf:uploadFiles){
StringBuffer sb=new StringBuffer();
sb.append(SEPARATOR);
sb.append(BREAKLINE);
sb.append("Content-Disposition: form-data; name=\""+uf.getParameterName()+"\"; filename=\""+ uf.getFilename()+"\"");
sb.append(BREAKLINE);
sb.append("Content-Type: "+uf.getContentType());
sb.append(BREAKLINE).append(BREAKLINE);
//以上是这个文件的文本信息部分
oos.write(sb.toString().getBytes());
if(uf.getInStream()!=null){
//以流的形式先读,再用oos写
byte[] buffer=new byte[1024];
int length=-1;
while((length=uf.getInStream().read(buffer,0,buffer.length))!=-1){
oos.write(buffer,0,length);
}
}else{
oos.write(uf.getData(),0,uf.getData().length);
}
oos.write(BREAKLINE.getBytes());
oos.flush();
}
}
//还有一个结束行
oos.write(ENDLINE.getBytes());
oos.flush();
//接收服务器的回应
InputStream iis=s.getInputStream();
String result=new String();
int len=-1;
byte[] b=new byte[1024];
while((len=iis.read(b,0,b.length))!=-1){
result+=new String(b,0,len);
}
oos.close();
iis.close();
s.close();
return result;
}
}
我用的是handler类通过消息机制操作线程,也是可以通过AsyncTask操作。
线程代码如下:
btn4.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
new Thread(new Runnable() {
@Override
public void run() {
Mapparams=new HashMap();
Person p =(Person)FilesUtils.getObjectFromFile("xiaofeng", MainActivity2.this);
params.put("name", p.getName());
params.put("age", p.getAge()+"");
params.put("sex", p.getSex());
ListuploadFiles=new ArrayList();
File headFile=new File(p.getHeadpic());
String filename=headFile.getName();
uploadFiles.add(new UploadFile(headFile, filename, "image/jpeg", "headpic"));
WebUtil wu=new WebUtil();
try {
String responseprotocol=wu.postFileToServer("http://211.69.255.66/ownUploadServer/doupload.action", params, uploadFiles);
Message m=new Message();
m.what=1;
m.obj=responseprotocol;
handler.sendMessage(m);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Hannler的实例如下:
private Handler handler=new Handler(){
public void handleMessage(android.os.Message msg) {
switch(msg.what){
case 1:
String response=(String) msg.obj;
if(response.indexOf("200")>0){
Toast.makeText(MainActivity2.this, "上传云端成功", 3000).show();
}
}
}
};
值得一提的是:android在发送数据到服务器的时候,一定不能写localhost,因为localhost已经被android模拟器占用了,只能用ip地址,而且如果是真机测试的话,还要保证手机的网段和电脑的网段一致,并且关闭防火墙。才能测试成功。。
通过学习android的网络存储,对协议有更深的了解了,加油