欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-08 11:25:04
没有借助任何第三方库,完全基于java socket实现一个最小化的http文件下载客户端。完整的演示如何通过socket实现下载文件的http请求(request hea...

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

首先看一下ui部分:

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

【添加下载】按钮:

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

下载

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

【清除完成】按钮:

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

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

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请求报文如

Java基于Socket实现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响应报文如下

Java基于Socket实现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程序设计有所帮助。