第四天

本文详细介绍如何使用HTTP协议的GET和POST方法提交数据到服务器,包括使用httpURLConnection、HttpClient及AsyncHttpClient等方式的具体实现,并探讨了多线程下载及断点续传的技术实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、httpURLConnection  方式把数据提交到服务器
    1、搭建服务器端程序
    2、使用get方法和post服务方法提交数据
        1、使用get方法提交数据
   
  1. //获取用户名,密码
  2. String username = et_username.getText().toString().trim();
  3. String pwd = et_password.getText().toString().trim();
  4. // 定义访问的URL
  5. final String path = "http://192.168.128.2:8080/login/servlet/LoginServelet?username="+username+"&password="+pwd+"";
  6. //错误点:将协议名称忘了
  7. //开启子线程,进行网络访问
  8. new Thread(){
  9. public void run(){
  10. try {
  11. URL url = new URL(path);
  12. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  13. connection.setRequestMethod("GET");
  14. connection.setReadTimeout(5000);
  15. int responseCode = connection.getResponseCode();
  16. if(responseCode == 200){ //200请求资源全部成功 206请求资源部分成功
  17. InputStream inputStream = connection.getInputStream();
  18. String res = StreamUtils.fromStream(inputStream);
  19. showToast(res);
  20. }
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }.start();
        2、使用post方法提交数据
    
  1. //获取用户名和密码
  2. String username = et_username.getText().toString().trim();
  3. String pwd = et_password.getText().toString().trim();
  4. //get和post不同点一★★★★★★★设置访问路径
  5. final String path = "http://192.168.128.2:8080/login/servlet/LoginServelet";
  6. //访问网络资源,开启子线程
  7. new Thread(){
  8. public void run(){
  9. try {
  10. URL url = new URL(path);
  11. HttpURLConnection connection = (HttpURLConnection)url.openConnection();
  12. //get和post不同点二★★★★★★★组拼请求体
  13. String data = "username=abc&password=123";
  14. //设置访问属性
  15. connection.setRequestMethod("POST");
  16. connection.setReadTimeout(5000);
  17. //get和post不同点三★★★★★★★另外设置的两个请求属性
  18. //错误点:忘记将Content-Type:和Content-Length:的":"去掉,因而服务器返回了500状态码
  19. connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
  20. connection.setRequestProperty("Content-Length",data.length()+"");
  21. //get和post不同点四★★★★★★★设置允许输出,并且将数据输出
  22. connection.setDoOutput(true);
  23. connection.getOutputStream().write(data.getBytes());
  24. //获取状态码
  25. int responseCode = connection.getResponseCode();
  26. if(responseCode == 200){
  27. InputStream inputStream = connection.getInputStream();
  28. String s = StreamUtils.fromStream(inputStream);
  29. showToast(s);
  30. }
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }.start();
      3、get方式和post方式不同点
            1、请求的url地址上不同  在get方式上是通过组拼url地址的方式,将数据全部都存到url地址上,但是大小是有限制的,有两种限制一种是最大数据请求量为1k另外一个是4k,1k是由浏览器规定,4k是由http协议规定。而post方式,请求信息放在请求体中
            2、post方式多了两个属性和一个请求体
                属性是 Content-Type Content-Length 两个属性,
                    多了一个请求体 String data = "username=abc&password=123" ;
2、乱码解决
        乱码原因:
         *         从android客户端提交上来的数据默认使用android的utf-8编码方式,在传输到接受的过程中是先在android客户端使用utf-8码表编码,到达                    tomcat服务器,使用iso8859-1解码,由于码表不对应
         *         所以出现乱码,
         *         String username = req.getParameter("username");   使用iso8859-1进行解码,所以要再进行构造,然后使用utf-8进行编码
         *         new String(username.getBytes("iso8859-1"),"utf-8")  现使用iso8859-1进行解码,然后使用utf-8进行编码
         *         整个过程是utf-8编码--->iso8859-1解码---->iso8859-1进行编码为原来的二进制--->utf-8进行解码就是原来的数据
    解决代码
   
  1. //首先将数据先使用URLEncode进行一下url编码
  2. String data = "username="+URLEncoder.encode(username,"UTF-8")+"&password="+URLEncoder.encode(pwd,"UTF-8");
  3. //再在服务器端进行解码编码再解码
     
  1. new String(username.getBytes("iso8859-1"),"utf-8")

3、以HttpClient方式把数据提交到服务器
    
    [1]  HttpClient是apache的一个开源项目,然后由谷歌封装,作为一个类出现在android的开发中
    [2] HttpClient的使用
    在android api level23之后,google将DefaultHttpClient废弃了,然后鼓励使用HttpURLConnection进行开发,如果想要使用,那么需要去23以后的 optional 中找到org.apache.http.legacy.jar,然后导入库文件,就是粘到lib文件夹下,然后才能使用
    
  1. //使用get请求发送数据 
  2. // [1] 获取用户名和密码
  3. String username = et_username.getText().toString().trim();
  4. String password = et_password.getText().toString().trim();
  5. // [2] 组拼URL地址
  6. final String path = "http://192.168.128.2:8080/login/servlet/LoginServelet?username="+username+"&password="+password+"";
  7. // [3] 开启子线程进行网络访问
  8. new Thread(){
  9. public void run() {
  10. //获取执行端
  11. DefaultHttpClient client = new DefaultHttpClient();
  12. //获取get执行方法
  13. final HttpGet get = new HttpGet(path);
  14. //开启子线程执行网络请求
  15. try {
  16. HttpResponse response = client.execute(get);
  17. int code = response.getStatusLine().getStatusCode();
  18. if(code == 200){
  19. InputStream inputStream = response.getEntity().getContent();
  20. String s = StreamUtils.fromStream(inputStream);
  21. showToast(s);
  22. }
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }.start();
  28.   // [4] 加上网络访问权限
  29. <uses-permission android:name="android.permission.INTERNET"/>
     
  1.  //使用post请求进行服务器访问
  2. // [1] 准备用户名和密码
  3. String username = et_username.getText().toString().trim();
  4. String password = et_password.getText().toString().trim();
  5. // [2] 准备路径
  6. String path = "http://192.168.128.2:8080/login/servlet/LoginServelet";
  7. // [3] 准备提交端
  8. DefaultHttpClient client = new DefaultHttpClient();
  9. HttpPost post = new HttpPost(path);
  10. //[3.1] 准备提交的数据
  11. List<NameValuePair> lists = new ArrayList<NameValuePair>();
  12. BasicNameValuePair nameValuePair = new BasicNameValuePair("username", username);
  13. BasicNameValuePair pwdValuePair1 = new BasicNameValuePair("password", password);
  14. // [3.2] 将数据添加到list中
  15. lists.add(nameValuePair);
  16. lists.add(pwdValuePair1);
  17. // [3.3] 生成数据实体
  18. UrlEncodedFormEntity entity = new UrlEncodedFormEntity(lists);
  19. // [3.4] 设置访问数据
  20. post.setEntity(entity);
  21. // [3.5] 执行语句
  22. client.execute(post);
  23. HttpResponse response = client.execute(post);
  24. int code = response.getStatusLine().getStatusCode();
  25. if(code == 200){
  26. InputStream inputStream = response.getEntity().getContent();
  27. String s = StreamUtils.fromStream(inputStream);
  28. showToast(s);
  29. }
  [3] 使用post方式提交数据的代码编写思路
       [1]  由于需要上传数据 ,所以需要设置entity post . setEntity ( entity );
       [2]  实体需要Entity,所以要实例化一下  UrlEncodedFormEntity entity = new UrlEncodedFormEntity ( lists );
       [3]  需要加载数据,这个数据是一个list的nameValuePair的,
       [4]   由于NameValuePair是一个interface,所以需要实例化它的子类 
       BasicNameValuePair pwdValuePair1 = new BasicNameValuePair ( "password" , password );
       [5] 然后将这些数据放到list中就可以开始访问了
4、开源项目方式把数据提交到服务器
    AsyncHttpClient方式提交数据
    [1] get方式提交数据
    
  1. // [1] 获取用户名和密码
  2. String username = et_username.getText().toString().trim();
  3. String password = et_password.getText().toString().trim();
  4. // [2] 组拼URL地址
  5. String path = "http://192.168.128.2:8080/login/servlet/LoginServelet?username="+username+"&password="+password+"";
  6. AsyncHttpClient client = new AsyncHttpClient();
  7. client.get(path, new AsyncHttpResponseHandler() {
  8. @Override
  9. public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
  10. Toast.makeText(getApplicationContext(),new String(responseBody),Toast.LENGTH_LONG).show();
  11. }
  12. @Override
  13. public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
  14. }
  15. });
    [2]  post方式提交数据
    
  1. // [1] 准备用户名和密码
  2. String username = et_username.getText().toString().trim();
  3. String password = et_password.getText().toString().trim();
  4. // [2] 准备路径
  5. String path = "http://192.168.128.2:8080/login/servlet/LoginServelet";
  6. AsyncHttpClient client = new AsyncHttpClient();
  7. RequestParams params = new RequestParams();
  8. params.put("username",username);
  9. params.put("password",password);
  10. client.post(path, params, new AsyncHttpResponseHandler() {
  11. @Override
  12. public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
  13. Toast.makeText(getApplicationContext(),new String(responseBody),Toast.LENGTH_LONG).show();
  14. }
  15. @Override
  16. public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
  17. }
  18. });
    总结
    [1] HttpURLConnection   可以人随意封装信息
    [2] HttpClient    
    [3] AsyncHttpClient    在只上传一些简单的信息的时候使用,例如账号密码等
5、javase多线程下载
    概念:开辟多个线程,精确的分段下载文件,然后将文件合并
      元数据 Metadata ),又称 中介数据 中继数据 ,为描述 数据 的数据(data about data),主要是描述数据 属性 (property)的 信息 ,用来支持如指示存储位置、 历史 数据、 资源 查找、文件记录等功能
    步骤与实现代码
    
   
  1. URL url = new URL(path);
  2. HttpURLConnection connection = (HttpURLConnection)url.openConnection();
  3. connection.setRequestMethod("GET");
  4. connection.setReadTimeout(5000);
  5. int resuCode = connection.getResponseCode();
  6. if(resuCode == 200){
  7. //获取服务器文件大小,以便计算线程的开始和结束的位置
  8. int length = connection.getContentLength();
  9. //创建一个文件大小和服务器文件一样,提前把空间申请出来
  10. /*System.out.println(length); //9173224*/
  11. RandomAccessFile raf = new RandomAccessFile("temp.exe", "rw");
  12. //设置文件大小
  13. raf.setLength(length);
  14. raf.close();
  15. //计算出每个下载文件大约大小
  16. int blockSize = length / THREADCOUNT;
  17. //找出每个文件大开始位置和结束位置
  18. for(int i = 0;i < THREADCOUNT;i++){
  19. int startIndex = i * blockSize;
  20. int endIndex = (i+1) * blockSize - 1;
  21. //这是特殊情况,是最后一个线程,那么就将剩下的内容全部下载下来
  22. if(i == THREADCOUNT - 1){
  23. endIndex = length - 1;
  24. }
  25. //开线程去下载文件
  26. new DownloadThread(startIndex, endIndex, i).start();
    
  1. URL url = new URL(path);
  2. HttpURLConnection connection = (HttpURLConnection)url.openConnection();
  3. connection.setRequestMethod("GET");
  4. connection.setReadTimeout(5000);
  5. //设置一个头信息,代表分段下载
  6. connection.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
  7. int resultCode = connection.getResponseCode();
  8. if(resultCode == 206){ //206代表部分资源请求成功
  9. RandomAccessFile raf = new RandomAccessFile("temp.exe", "rw");
  10. raf.seek(startIndex);
  11. InputStream inputStream = connection.getInputStream();
  12. int len = -1;
  13. byte[] buff = new byte[1024];
  14. while((len = inputStream.read(buff)) != -1){
  15. raf.write(buff,0,len);
  16. }
  17. raf.close();
  18. System.out.println("线程"+threadId+"--下载完成");

6、断点续传实现
    这个是在多线程下载的基础上,添加上了这几个逻辑
    [1]  在重新下载的时候读取临时记录文件,看是不是存在,如果存在,那么将这个值作为开始下载的值继续进行
    
  1. //如果以前出了意外导致文件没有下载完,那么就可以读取记录文件,进行继续下载
  2. File file = new File(getFileName(path)+"."+threadId+".downloadTempFile");
  3. if(file.exists() && file.length() > 0){
  4. BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
  5. String lastPosition = bfr.readLine();
  6. System.out.println("上次"+threadId+"下载到"+lastPosition);
  7. startIndex = Integer.valueOf(lastPosition);
  8. }
    [2]  在每次获取并写入后,将读取到的位置写到临时记录文件中
    
  1. //获取当前线程的下载到的位置
  2. total += len;
  3. long currentThreadPosition = startIndex + total;
  4. //将下载的位置存起来,以备断点后继续下载 并且文件的存储必须是每次更新都写到底层的设备,使用mode=rwd
  5. RandomAccessFile raff = new RandomAccessFile(getFileName(path)+"."+threadId+".downloadTempFile", "rwd");
  6. //这里犯了一个错误raf.write(int len) 是将从文件指针位置开始计算写len个字节进文件,并不是将len写进去
  7. raff.write(String.valueOf(currentThreadPosition).getBytes());
  8. raff.close();
    [3] 如果全部下载完成,那么将临时记录文件全部删除
    
   
  1. //一旦全部下载完成就将所有的临时文件删除
  2. /*
  3. * 这里的锁加不加都无所谓,因为最后是一个判断,无论怎么减都不会出现-1,最后结果都是0而且减的结果
  4. * 都是唯一的,
  5. * 但是如果是在每个线程中都在使用,例如卖票,先打印票剩余的张数再进行--
  6. * System.out.println(runningThread);
  7. * runningThread--;
  8. * 这样就会出问题,因为如果thread-0先打印了,然后cpu切了出去,那么还会打印一遍这个值,就会出问题
  9. * 这个时候就需要使用锁了
  10. */
  11. synchronized(DownloadThread.class){
  12. runningThread--;
  13. if(runningThread == 0){
  14. for(int i = 0;i < THREADCOUNT;i++){
  15. File deleteFile = new File(getFileName(path)+"."+i+".downloadTempFile");
  16. deleteFile.delete();
  17. }
  18. }
  19. }

7、断点续传逻辑移植到android上

8、开源项目实现多线程下载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值