当前位置: 移动技术网 > IT编程>开发语言>Java > Java基于Socket实现HTTP下载客户端

Java基于Socket实现HTTP下载客户端

2019年07月22日  | 移动技术网IT编程  | 我要评论

没有借助任何第三方库,完全基于java socket实现一个最小化的http文件下载客户端。完整的演示如何通过socket实现下载文件的http请求(request header)发送如何从socket中接受http响应(response header, response body)报文并解析与保存文件内容。如何通过swingwork实现ui刷新,实时显示下载进度。

首先看一下ui部分:

【添加下载】按钮:

点击弹出url输入框,用户copy要下载文件url到输入框以后,点击[ok]按钮即开始

下载


【清除完成】按钮:

清除所有已经下载完成的文件列表

文件下载状态分为以下几种:

package com.gloomyfish.socket.tutorial.http.download; 
 
public enum downloadstatus { 
  not_started, 
  in_process, 
  completed, 
  error 
} 

ui部分主要是利用swing组件完成。点击【添加下载】执行的代码如下:

final jdialog dialog = new jdialog(this,"add file link",true); 
dialog.getcontentpane().setlayout(new borderlayout()); 
// dialog.setsize(new dimension(400,200)); 
final urlfilepanel panel = new urlfilepanel(); 
panel.setuplistener(new actionlistener(){ 
  @override 
  public void actionperformed(actionevent e) { 
    if("ok".equals(e.getactioncommand())){ 
      if(panel.validateinput()) { 
        downloaddetailstatusinfomodel data = new downloaddetailstatusinfomodel(panel.getvalidfileurl()); 
        tablemodel.getdata().add(data); 
        startdownlaod(); 
        refreshui(); 
      } 
      dialog.setvisible(false); 
      dialog.dispose(); 
    } else if("cancel".equals(e.getactioncommand())) { 
      dialog.setvisible(false); 
      dialog.dispose(); 
    } 
  }}); 
 
dialog.getcontentpane().add(panel, borderlayout.center); 
dialog.pack(); 
centre(dialog); 
dialog.setvisible(true); 

【清除完成】按钮执行的代码如下:

private void cleardownloaded() { 
  list<downloaddetailstatusinfomodel> downloadedlist = new arraylist<downloaddetailstatusinfomodel>(); 
  for(downloaddetailstatusinfomodel filestatus : tablemodel.getdata()) { 
    if(filestatus.getstatus().tostring().equals(downloadstatus.completed.tostring())) { 
      downloadedlist.add(filestatus); 
    } 
  } 
  tablemodel.getdata().removeall(downloadedlist); 
  refreshui(); 
} 

让jframe组件居中显示的代码如下:

public static void centre(window w) { 
  dimension us = w.getsize(); 
  dimension them = toolkit.getdefaulttoolkit().getscreensize(); 
  int newx = (them.width - us.width) / 2; 
  int newy = (them.height - us.height) / 2; 
  w.setlocation(newx, newy); 
} 

http协议实现部分:

概述:http请求头与相应头报文基本结构与解释

http请求:一个标准的http请求报文如


其中请求头可以有多个,message-body可以没有,不是必须的。请求行的格式如下:

request-line = method sp request-uri sphttp-version crlf 举例说明如下:

request-line = get http://www.w3.org/pub/www/theproject.htmlhttp/1.1\r\n

其中sp表示空格, crlf表示回车换行符\r\n

当你想要上传文件时候,使用post方式来填写数据到message-body中即可。发送一个

简单的http请求报文如下:

  • get /pub/www/theproject.html http/1.1\r\n
  • host:
  • \r\n

http响应:一个标准的http响应报文如下


最先得到是状态行,其格式如下:

status-line = http-version sp status-codesp reason-phrase crlf, 一个状态行的简单例子如下:status-line = http/1.1 200 ok一般大家最喜欢的就是status-code会给你很多提示,最常见的就是404,500等状态码。状态码的意思可以参考rfc2616中的解释。下载文件最要紧是的检查http响应头中的content-length与content-type两

个中分别声明了文件的长度与文件的类型。其它如accept-ranges表示接受多少到多少的字节。可能在多线程下载中使用。搞清楚了http请求与响应的报文格式以后,我们就可以通过socket按照报文格式解析内容,发送与读取http请求与响应。具体步骤

如下:

一、根据用户输入的文件url建立socket连接

url url = new url(fileinfo.getfileurl()); 
string host = url.gethost(); 
int port = (url.getport() == -1) ? url.getdefaultport():url.getport(); 
system.out.println("host name = " + host); 
system.out.println("port = " + port); 
system.out.println("file uri = " + url.getfile()); 
 
// create socket and start to construct the request line 
socket socket = new socket(); 
socketaddress address = new inetsocketaddress(host, port); 
socket.connect(address); 

用了url类来把用户输入的url string变成容易解析一点的url。
二、构造http请求

bufferedwriter bufferedwriter = new bufferedwriter(new outputstreamwriter(socket.getoutputstream(), "utf8")); 
string requeststr = "get " + url.getfile() + " http/1.1\r\n"; // request line 
 
// construct the request header - 构造http请求头(request header) 
string hostheader = "host: " + host + "\r\n"; 
string acceptheader = "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"; 
string charsetheader = "accept-charset: gbk,utf-8;q=0.7,*;q=0.3\r\n"; 
string languageheader = "accept-language: zh-cn,zh;q=0.8\r\n"; 
string keepheader = "connection: close\r\n"; 

三、发送http请求

// 发送http请求 
bufferedwriter.write(requeststr); 
bufferedwriter.write(hostheader); 
bufferedwriter.write(acceptheader); 
bufferedwriter.write(charsetheader); 
bufferedwriter.write(languageheader); 
bufferedwriter.write(keepheader); 
bufferedwriter.write("\r\n"); // 请求头信息发送结束标志 
bufferedwriter.flush(); 

四、接受http响应并解析内容,写入创建好的文件

// 准备接受http响应头并解析 
customdatainputstream input = new customdatainputstream(socket.getinputstream()); 
file myfile = new file(fileinfo.getstorelocation() + file.separator + fileinfo.getfilename()); 
string content = null; 
httpresponseheaderparser responseheader = new httpresponseheaderparser(); 
bufferedoutputstream output = new bufferedoutputstream(new fileoutputstream(myfile)); 
boolean hasdata = false; 
while((content = input.readhttpresponseheaderline()) != null) { 
  system.out.println("response header contect -->> " + content); 
  responseheader.addresponseheaderline(content); 
  if(content.length() == 0) { 
    hasdata = true; 
  } 
  if(hasdata) { 
    int totalbytes = responseheader.getfilelength(); 
    if(totalbytes == 0) break; // no response body and data 
    int offset = 0; 
    byte[] mydata = null; 
    if(totalbytes >= 2048) { 
      mydata = new byte[2048]; 
    } else { 
      mydata = new byte[totalbytes]; 
    } 
    int numofbytes = 0; 
    while((numofbytes = input.read(mydata, 0, mydata.length)) > 0 && offset < totalbytes) { 
      offset += numofbytes; 
      float p = ((float)offset) / ((float)totalbytes) * 100.0f; 
      if(offset > totalbytes) { 
        numofbytes = numofbytes + totalbytes - offset; 
        p = 100.0f; 
      } 
      output.write(mydata, 0, numofbytes); 
      updatestatus(p); 
    } 
    hasdata = false; 
    break; 
  } 
} 

简单的http响应头解析类httpresponseheaderparser代码如下:

package com.gloomyfish.socket.tutorial.http.download; 
 
import java.util.hashmap; 
import java.util.map; 
 
/** 
 * it can parse entity header, response head 
 * and response line <status code, charset, ect...> 
 * refer to rfc2616,关于http响应头,请看rfc文档,描写的很详细啊!! 
 */ 
public class httpresponseheaderparser { 
  public final static string content_length = "content-length"; 
  public final static string content_type = "content-type"; 
  public final static string accept_ranges = "accetp-ranges"; 
   
  private map<string, string> headermap; 
  public httpresponseheaderparser() { 
    headermap = new hashmap<string, string>(); 
  } 
  /** 
   * <p> get the response header key value pair </p> 
   * @param responseheaderline 
   */ 
  public void addresponseheaderline(string responseheaderline) { 
    if(responseheaderline.contains(":")) { 
      string[] keyvalue = responseheaderline.split(": "); 
      if(keyvalue[0].equalsignorecase(content_length)) { 
        headermap.put(content_length, keyvalue[1]); 
      } else if(keyvalue[0].equalsignorecase(content_type)) { 
        headermap.put(content_type, keyvalue[1]); 
      } else { 
        headermap.put(keyvalue[0], keyvalue[1]); 
      } 
    } 
  } 
   
  public int getfilelength() { 
    if(headermap.get(content_length) == null){ 
      return 0; 
    } 
    return integer.parseint(headermap.get(content_length)); 
  } 
   
  public string getfiletype() { 
    return headermap.get(content_type); 
  } 
  public map<string, string> getallheaders() { 
    return headermap; 
  } 
 
} 

以上就是本文的全部内容,希望对大家的学习java程序设计有所帮助。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网