@Tyhj
        
        2016-12-06T07:25:24.000000Z
        字数 8228
        阅读 1696
    Java
自己写Android也已经两年了,平时和其他同学合作都是别人写后台,或者直接用第三方的Api,把数据存到别人服务器上面,自己也了解过后台就是自己从来没有去实现过,所以现在感觉没什么事情,就想要自己搞一下。 
之前考虑过用Python,但是还要去学习好久的基本语法,自己也是懒得去学了,还是来用Java好了。
环境配置什么的我就不想说了,很简单 
把Android端作为客户端,用eclipse或者Myeclipse写服务器端,需要环境:
反正能写Java,Android和Jsp,再安装个Tomcat就好了,Myeclipse应该自带了Tomcat,但是需要破解,其他三个是开源的。
新建一个web project工程
在写java的文件夹下面选择新建一个servlet,新建好了以后应该是已经继承了HttpServlet并且重写了doGet和doPost方法
新建了servlet后现在还不能通过url来直接访问,必须先在web.xml里面配置后才可以访问,我的配置如下
<servlet><servlet-name>sign_up</servlet-name><servlet-class>servlet.Signup</servlet-class></servlet><servlet-mapping><servlet-name>sign_up</servlet-name><url-pattern>/talk/sign_up</url-pattern></servlet-mapping>
在WebContent/WEB-INF下面,如果找不到就是创建工程的时候没有选择生成,需要重新创建工程并选择生成web.xml,每个软件或者版本都可能不一样
就是两个标签, 
其中servlet-name必须一样,servlet-class就是这个类的路径,就是包名.类名, 
url-pattern就是url有关,可以随便写,比如我的要访问这个servlet,url就是
其中192.168.31.215是你的ip地址 
8080是端口,tomcat端口一般是8080 
Talk是项目名
本地测试的,让电脑和手机连接同一个WiFi,然后在终端输入,就看到了ip地址,
mac:ifconfig在en0下面:inet 192.168.31.215 netmask 0xffffff00 broadcast
windows:ipconfig应该是IPv4:无线局域网适配器 WLAN:IPv4 地址 . . . . . . . . . . . . : 192.168.31.104子网掩码 . . . . . . . . . . . . : 255.255.255.0默认网关. . . . . . . . . . . . . : 192.168.31.1
在doGet和doPost里面接收数据,具体在那个里面自己随便写,会决定客户端请求的方式,可以先看一下Http协议的get和post的区别。
接收格式为:
request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");String username = request.getParameter("username");String password = request.getParameter("password");
其中username和password就是在客户端发来的数据。
Android studio的编码是UTF-8,所以我们设置编码格式避免乱码
返回数据给客户端,好让客户端知道请求成功没有,或者返回所请求的资源。这里一般是要用一种固定格式的,以便于客户端好解析,所以我们这里用json格式来包装数据,当然需要导入jar包,下载,复制到项目lib目录,然后,build to path就好了,我用的是google-gson,下载地址为:
http://repo1.maven.org/maven2/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar
现在随便返回一些值,把数据封装到json中:
JsonObject json = new JsonObject();json.addProperty("code", "1");json.addProperty("name", username);json.addProperty("pas", password);
然后把json转成字符串
String postJson =json.toString();
最后返回给客户端
PrintWriter out = response.getWriter();out.write(postJson);out.close();
这样的过程应该是比较合理的,客户端拿到数据后就可以轻松地解析出数据,开头一般设置一个状态码,让用户知道请求是不是成功了。
新建一个Android项目
新建一个类叫Myhttp,用于保存一些发送http请求的方法
在Android中有几种不同的方法发起http请求,我会用HttpURLConnection方法来请求。post和get请求的方法也是不一样的
public static JSONObject getJson_Post(String data, String url) {HttpURLConnection conn = null;URL mURL = null;try {mURL = new URL(url);conn = (HttpURLConnection) mURL.openConnection();conn.setRequestMethod("POST");conn.setReadTimeout(5000);conn.setConnectTimeout(10000);conn.setDoOutput(true);OutputStream out = conn.getOutputStream();out.write(data.getBytes());out.flush();out.close();int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方if (responseCode == 200) {//获取到的返回值InputStream is = conn.getInputStream();//将返回值转化成字符串String state = getStringFromInputStream(is);Log.e("Tag",state);//将字符串转化成Json格式JSONObject jsonObject = new JSONObject(state);if (jsonObject != null)return jsonObject;}return null;} catch (Exception e) {e.printStackTrace();return null;}}
public static JSONObject getJson_Get(String data, String url) {HttpURLConnection conn = null;URL mURL = null;try {mURL = new URL(url + "?" + data);conn = (HttpURLConnection) mURL.openConnection();conn.setRequestMethod("GET");conn.setReadTimeout(5000);conn.setConnectTimeout(10000);InputStream is = conn.getInputStream();String state = getStringFromInputStream(is);//Log.e("Tag",state);JSONObject jsonObject = new JSONObject(state);if (jsonObject != null)return jsonObject;return null;} catch (Exception e) {e.printStackTrace();return null;}}
这是基本的http请求,我们可以在任何Post和Get请求调用它,我们请求成功后拿到返回值再根据实际功能进行解析,其中获取到的字符串是这个样子的:
{"code":"1","name":"嗨","pas":"你好"}
首先要开启访问网络权限,然后开启线程,在线程中执行,get就是执行doGet,Post同理。 
其中data就是你要传给服务器的参数,url就是地址。
String url="http://192.168.31.215:8080/Talk/talk/sign_up";new Thread(new Runnable() {@Overridepublic void run() {JSONObject jsonObject=MyHttp.getJson_Post("username=嗨&password=你好",url);try {String code=jsonObject.getString("code");String name=jsonObject.getString("name");String pas=jsonObject.getString("pas");} catch (JSONException e) {e.printStackTrace();}}}).start();
这样就完成了基本的数据传输。
使用Apache开发的文件上传处理库Commons FileUpload来进行文件的上传,这个库还可以,有很多优点,上传的文件可大可小。 
需要这俩jar包 
commons-fileupload.jar:
下载地址:http://apache.fayea.com//commons/fileupload/binaries/commons-fileupload-1.3.2-bin.zip
commons-io.jar
下载地址:http://mirrors.hust.edu.cn/apache//commons/io/binaries/commons-io-2.5-bin.zip
新建一个servlet,然后在web.xml中配置好
<servlet><servlet-name>upload</servlet-name><servlet-class>servlet.Upload</servlet-class></servlet><servlet-mapping><servlet-name>upload</servlet-name><url-pattern>/talk/setHeadImage</url-pattern></servlet-mapping>
System.out.println(name + "doPost+被访问");request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");//获得磁盘文件条目工厂。DiskFileItemFactory factory = new DiskFileItemFactory();//获取文件上传需要保存的路径,upload文件夹需存在。String path ="/Users/Tyhj/softWare/upload";//设置暂时存放文件的存储室,这个存储室可以和最终存储文件的文件夹不同。因为当文件很大的话会占用过多内存所以设置存储室。factory.setRepository(new File(path));//设置缓存的大小,当上传文件的容量超过缓存时,就放到暂时存储室。factory.setSizeThreshold(1024*1024);//上传处理工具类(高水平API上传处理?)ServletFileUpload upload = new ServletFileUpload(factory);try{//调用 parseRequest(request)方法 获得上传文件 FileItem 的集合list 可实现多文件上传。List<FileItem> list = (List<FileItem>)upload.parseRequest(request);for(FileItem item:list){//获取表单属性名字。String name = item.getFieldName();//如果获取的表单信息是普通的文本信息。即通过页面表单形式传递来的字符串。if(item.isFormField()){//获取用户具体输入的字符串,String value = item.getString();request.setAttribute(name, value);}//如果传入的是非简单字符串,而是图片,音频,视频等二进制文件。else{//获取路径名String value = item.getName();//取到最后一个反斜杠。int start = value.lastIndexOf("\\");//截取上传文件的 字符串名字。+1是去掉反斜杠。String filename = value.substring(start+1);request.setAttribute(name, filename);/*第三方提供的方法直接写到文件中。* item.write(new File(path,filename));*///收到写到接收的文件中。OutputStream out = new FileOutputStream(new File(path,filename));InputStream in = item.getInputStream();int length = 0;byte[] buf = new byte[1024];System.out.println("获取文件总量的容量:"+ item.getSize());while((length = in.read(buf))!=-1){out.write(buf,0,length);}in.close();out.close();}}}catch(Exception e){e.printStackTrace();}JsonObject json = new JsonObject();json.addProperty("code", "1");json.addProperty("name", "上传成功");String postJson =json.toString();System.out.println(postJson);PrintWriter out = response.getWriter();out.write(postJson);out.close();
Android端也是比较简单,新建一个upLoad方法来执行文件上传的操作
//上传文件public static String upLoad(String url, File file) {String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成String PREFIX = "--", LINE_END = "\r\n";String CONTENT_TYPE = "multipart/form-data"; // 内容类型HttpURLConnection conn = null;java.net.URL mURL = null;try {mURL = new URL(url);conn = (HttpURLConnection) mURL.openConnection();conn.setRequestMethod("POST");conn.setReadTimeout(5000);conn.setConnectTimeout(10000);conn.setDoInput(true); // 允许输入流conn.setDoOutput(true); // 允许输出流conn.setUseCaches(false); // 不允许使用缓存conn.setRequestProperty("connection", "keep-alive");conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary="+ BOUNDARY);OutputStream out = conn.getOutputStream();DataOutputStream dos = new DataOutputStream(out);StringBuffer sb = new StringBuffer();sb.append(PREFIX);sb.append(BOUNDARY);sb.append(LINE_END);/*** 这里重点注意: name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件* filename是文件的名字,包含后缀名*/sb.append("Content-Disposition: form-data; name=\"file\"; filename=\""+ file.getName() + "\"" + LINE_END);sb.append("Content-Type: application/octet-stream; charset="+ CHARSET + LINE_END);sb.append(LINE_END);dos.write(sb.toString().getBytes());InputStream is = new FileInputStream(file);byte[] bytes = new byte[1024];int len = 0;while ((len = is.read(bytes)) != -1) {dos.write(bytes, 0, len);}is.close();dos.write(LINE_END.getBytes());byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();dos.write(end_data);dos.flush();int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方if (responseCode == 200) {InputStream input = conn.getInputStream();String state = For2mat.getInstance().inputStream2String(input);//Log.e("Tag",state);return state;}return null;} catch (Exception e) {e.printStackTrace();return null;}}
之前使用过把文件转化成字符串来上传的方法,但是上传的文件大小有限制,还容易出错,所以这个方法还可以,我也是网上找的代码,没有深入学习过,我测试上传了一个MV,是可以用的,当然我的代码有很多需要优化的地方,服务器返回值应该在文件保存之前返回,不然文件太大的话,会造成请求超时。
其实还有种简单的方法是直接把文件上传到第三方平台,用的时候直接取,这样其实也不错。
参考文章:http://blog.csdn.net/crazy__chen/article/details/41958701