Without any third-party libraries, a minimal HTTP file download client is implemented entirely based on JAVA Socket. A complete demonstration of how to implement HTTP request header for downloading files through Socket to send a HTTP response (Response header, Response body) packet from Socket and parse and save file contents. How to realize UI refresh through SwingWork to display download progress in real time.
First look at the UI part:
【Add Download】button:
Click the pop-up URL input box. After the user wants to download the file URL to the input box, click the [OK] button to start.
download
【Clear Complete】button:
Clear all downloaded files list
File download status is divided into the following types:
package com.gloomyfish.socket.tutorial.http.download; public enum DownloadStatus { NOT_STARTED, IN_PROCESS, COMPLETED, ERROR } The UI part is mainly done using Swing components. Click [Add Download] to execute the code as follows:
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); The code executed by the [Clear Complete] button is as follows:
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(); } The code for centering the JFrame component is as follows:
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 protocol implementation part:
Overview: Basic structure and explanation of HTTP request header and corresponding header packets
HTTP request: A standard HTTP request packet such as
There can be multiple request headers, and the message-body can be none, which is not necessary. The format of the request line is as follows:
Request-Line = Method SP Request-URI SPHTTP-Version CRLF gives an example as follows:
Request-Line = GET http://www.w3.org/pub/WWW/TheProject.htmlHTTP/1.1/r/n
where SP represents spaces, CRLF represents carriage return line breaks/r/n
When you want to upload a file, use Post to fill in the data into the message-body. Send one
A simple HTTP request message is as follows:
HTTP response: A standard HTTP response message is as follows
The first thing you get is the status line, its format is as follows:
Status-Line = HTTP-Version SP Status-CodeSP Reason-Phrase CRLF, a simple example of a status line is as follows: Status-Line = HTTP/1.1 200 OK Generally, everyone's favorite is Status-Code which will give you many prompts, the most common ones are status codes such as 404, 500, etc. The meaning of the status code can be referred to the explanation in RFC2616. The most important thing to download a file is to check the Content-Length and Content-Type in the HTTP response header.
The length and type of file are declared separately. Others, such as Accept-Ranges, represent how many to how many bytes are accepted. Possibly used in multi-threaded downloads. After understanding the packet format of HTTP request and response, we can parse the content in the packet format through Socket, send and read HTTP request and response. Specific steps
as follows:
1. Establish a Socket connection based on the file URL entered by the user
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); The URL class is used to turn the URL string entered by the user into a URL that is easier to parse.
2. Construct HTTP requests
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 - Construct the HTTP request header (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";
3. Send HTTP request
// Send HTTP request bufferedWriter.write(requestStr); bufferedWriter.write(hostHeader); bufferedWriter.write(acceptHeader); bufferedWriter.write(charsetHeader); bufferedWriter.write(languageHeader); bufferedWriter.write(keepHeader); bufferedWriter.write("/r/n"); // Request header message sending end flag bufferedWriter.flush(); 4. Accept HTTP response and parse content, and write to the created file
// Prepare to accept HTTP response headers and parse 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 contact -->> " + 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; } } The simple HTTP response header parsing class HttpResponseHeaderParser code is as follows:
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. For HTTP response headers, please see the RFC document, which is described in detail! ! */ 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; } }The above is all about this article, I hope it will be helpful for everyone to learn Java programming.