Http Downloader
//package org.terukusu.android.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.Header; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.CoreConnectionPNames; import android.util.Log; /** * Web??????????????????????? * * @author Teruhiko Kusunoki<<a * href="teru.kusu@gmail.com">teru.kusu@gmail.com</a>> * */ public class HttpDownloader { /** * TAG for {@link android.util.Log} */ private static final String TAG = HttpDownloader.class.getSimpleName(); public static enum Method { POST, GET } private static final int DEFAULT_BUFFER_SIZE = 4096; private static final int DEFAULT_CONNECTION_TIMEOUT = 10000; private static final int DEFAULT_SO_TIMEOUT = 10000; /** URL?????????????????? */ private String encoding = StringUtils.getDefaultEncoding(); private Method method = Method.GET; private String url; private Map<String, String> queries; private Map<String, String> cookies; private Map<String, String> headers; private int bufferSize = DEFAULT_BUFFER_SIZE; private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; private int soTimeout = DEFAULT_SO_TIMEOUT; /** ??????????Content-Length????? */ private long contentLength; /** ??????????????? */ private long elapsed; /** ??????????? */ private long downloaded; /** ??????(bps) */ private long avarageSpeed; /** ??????????????? */ private boolean isCanceled = false; /** * ???????????????? * */ public HttpDownloader() { } /** * ???????????????? * * @param url * URL??? */ public HttpDownloader(String url) { setUrl(url); } /** * ???????????????? * * @param url * URL * @param queries * ??? */ public HttpDownloader(String url, Map<String, String> queries) { this.url = url; this.queries = queries; } public HttpDownloader(String url, Map<String, String> queries, Map<String, String> cookies) { this.url = url; this.queries = queries; this.cookies = cookies; } /** * ???????????? * * @throws SocketTimeoutException * ???????????????????????? * @throws ClientProtocolException * ????????????????? * @throws IOException * ????????????? */ public HttpResponse execute() throws SocketTimeoutException, ClientProtocolException, IOException { long start = System.currentTimeMillis(); HttpUriRequest request = null; List<NameValuePair> postParams = new ArrayList<NameValuePair>(); if (Method.POST == getMethod()) { HttpPost post = new HttpPost(getUrl()); if (getQueries() != null) { for (Map.Entry<String, String> entry : getQueries().entrySet()) { postParams.add(new BasicNameValuePair(entry.getKey(), entry .getValue())); } } post.setEntity(new UrlEncodedFormEntity(postParams, getEncoding())); request = post; } else { StringBuilder sb = new StringBuilder(getUrl()); if (getQueries() != null) { if (sb.indexOf("?") >= 0) { sb.append('&'); } else { sb.append('?'); } sb.append(makeQueryString(getQueries())); } String fullUrl = sb.toString(); HttpGet get = new HttpGet(fullUrl); request = get; } // ???? if (getCookies() != null) { String cookieStr = makeCookieString(getCookies()); request.addHeader("Cookie", cookieStr); } if (getHeaders() != null) { for (Map.Entry<String, String> entry : getHeaders().entrySet()) { request.addHeader(entry.getKey(), URLEncoder.encode(entry.getValue(), getEncoding())); } } HttpClient client = new DefaultHttpClient(); client.getParams() .setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getConnectionTimeout()); client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, getSoTimeout()); org.apache.http.HttpResponse hr = null; HttpResponse res = new HttpResponse(); hr = client.execute(request); // ?? res.setStatusCode(hr.getStatusLine().getStatusCode()); // header for (Header header : hr.getAllHeaders()) { res.setHeader(header.getName(), header.getValue()); } // cookie for (Header header : hr.getHeaders("Set-Cookie")) { String cookieEntry = header.getValue(); Cookie cookie = Cookie.parseCookie(cookieEntry, null); res.setCookie(cookie.getKey(), cookie); } // body if (hr.getEntity() == null) { return res; } // ????? Header contentTypeHeader = hr.getEntity().getContentType(); if (contentTypeHeader != null) { String contentType = contentTypeHeader.getValue(); if (contentType != null) { Pattern p = Pattern.compile( "text/[^;]+(\\s*;\\s*charset\\s*=\\s*(.+))?", Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(contentType); if (m.find() && !StringUtils.isBlank(m.group(2))) { res.setCharacterEncoding(m.group(2)); } } } setContentLength(hr.getEntity().getContentLength()); res.setContentLength(hr.getEntity().getContentLength()); ByteArrayOutputStream baos = null; InputStream is = null; try { baos = new ByteArrayOutputStream(); is = hr.getEntity().getContent(); byte[] buff = new byte[getBufferSize()]; int readSize = 0; while (!isCanceled()) { if (Thread.interrupted()) { // ?????????? Thread.currentThread().interrupt(); synchronized (this) { isCanceled = true; } break; } if ((readSize = is.read(buff)) < 0) { // ?????? break; } synchronized (this) { setDownloaded(getDownloaded() + readSize); setElapsed(System.currentTimeMillis() - start); long elapsedSec = getElapsed() / 1000; if (elapsedSec == 0) { setAvarageSpeed(getDownloaded()); } else { setAvarageSpeed(getDownloaded() / elapsedSec); } } baos.write(buff, 0, readSize); } Log.v(TAG, "downloaded: avarage speed = " + getAvarageSpeed() + " bps, downloaded = " + getDownloaded() + " bytes"); if (isCanceled()) { // ?DL??DL??? request.abort(); } } finally { try { baos.close(); } catch (Exception ignore) { } try { is.close(); } catch (Exception ignore) { } } res.setBody(baos.toByteArray()); return res; } /** * ?????URL?????????????? * * @param params * ??? * @return ?????? */ private String makeQueryString(Map<String, String> params) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { if (sb.length() > 0) { sb.append("&"); } try { sb.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), getEncoding())); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } return sb.toString(); } /** * Cookie?????????????????????? * * @param cookies * ??????????????? * @return Cookie?????????? */ private String makeCookieString(Map<String, String> cookies) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : cookies.entrySet()) { if (sb.length() > 0) { sb.append("; "); } sb.append(entry.getKey()).append("=").append(entry.getValue()); } return sb.toString(); } /** * ???????????? */ public synchronized void cancel() { isCanceled = true; } /** * ???????(bps)??? * * @return ??????? */ public synchronized long getSpeed() { // TODO implement return 0; } /** * encoding ??????? * * @return encoding */ public synchronized String getEncoding() { return encoding; } /** * encoding ??????? * * @param encoding * ????? encoding */ public synchronized void setEncoding(String encoding) { this.encoding = encoding; } /** * method ??????? * * @return method */ public synchronized Method getMethod() { return method; } /** * method ??????? * * @param method * ????? method */ public synchronized void setMethod(Method method) { this.method = method; } /** * url ??????? * * @return url */ public synchronized String getUrl() { return url; } /** * url ??????? * * @param url * ????? url */ public synchronized void setUrl(String url) { this.url = url; } /** * queries ??????? * * @return queries */ public synchronized Map<String, String> getQueries() { return queries; } /** * queries ??????? * * @param queries * ????? queries */ public synchronized void setQueries(Map<String, String> queries) { this.queries = queries; } /** * elapsed ??????? * * @return elapsed */ public synchronized long getElapsed() { return elapsed; } /** * elapsed ??????? * * @param elapsed * ????? elapsed */ public synchronized void setElapsed(long elapsed) { this.elapsed = elapsed; } /** * bufferSize ??????? * * @return bufferSize */ public synchronized int getBufferSize() { return bufferSize; } /** * bufferSize ??????? * * @param bufferSize * ????? bufferSize */ public synchronized void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } /** * cookies ??????? * * @return cookies */ public synchronized Map<String, String> getCookies() { return cookies; } /** * cookies ??????? * * @param cookies * ????? cookies */ public synchronized void setCookies(Map<String, String> cookies) { this.cookies = cookies; } /** * contentLength ??????? * * @return contentLength */ public synchronized long getContentLength() { return contentLength; } /** * contentLength ??????? * * @param contentLength * ????? contentLength */ protected synchronized void setContentLength(long contentLength) { this.contentLength = contentLength; } /** * downloaded ??????? * * @return downloaded */ public synchronized long getDownloaded() { return downloaded; } /** * downloaded ??????? * * @param downloaded * ????? downloaded */ protected synchronized void setDownloaded(long downloaded) { this.downloaded = downloaded; } /** * isCanceled ??????? * * @return isCanceled */ public synchronized boolean isCanceled() { return isCanceled; } /** * isCanceled ??????? * * @param isCanceled * ????? isCanceled */ protected synchronized void setCanceled(boolean isCanceled) { this.isCanceled = isCanceled; } /** * avarageSpeed ??????? * * @return avarageSpeed */ public synchronized long getAvarageSpeed() { return avarageSpeed; } /** * avarageSpeed ??????? * * @param avarageSpeed * ????? avarageSpeed */ protected synchronized void setAvarageSpeed(long avarageSpeed) { this.avarageSpeed = avarageSpeed; } /** * connectionTimeout ??????? * * @return connectionTimeout */ public synchronized int getConnectionTimeout() { return connectionTimeout; } /** * connectionTimeout ??????? * * @param connectionTimeout * ????? connectionTimeout */ public synchronized void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } /** * soTimeout ??????? * * @return soTimeout */ public synchronized int getSoTimeout() { return soTimeout; } /** * soTimeout ??????? * * @param soTimeout * ????? soTimeout */ public synchronized void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; } /** * headers ??????? * * @return headers */ public synchronized Map<String, String> getHeaders() { return headers; } /** * headers ??????? * * @param headers * ????? headers */ public synchronized void setHeaders(Map<String, String> headers) { this.headers = headers; } } /** * @author Teruhiko Kusunoki<<a * href="teru.kusu@gmail.com">teru.kusu@gmail.com</a>> * */ class HttpResponse { private int statusCode; private long contentLength; private String characterEncoding; private final Map<String, String> headers = new HashMap<String, String>(); private final Map<String, Cookie> cookies = new HashMap<String, Cookie>(); private byte[] body; /** * */ public HttpResponse() { } public void setHeader(String name, String value) { this.headers.put(name, value); } /** * header ??????? * * @return headers */ public String getHeader(String name) { return this.headers.get(name); } /** * headers ??????? * * @return headers */ public Map<String, String> getHeaders() { return headers; } /** * ??????????? * * @param name * ?? * @return ???? */ public Cookie getCookie(String name) { return this.cookies.get(name); } /** * ??????????? * * @param name * ?? * @param cookie * ???? */ public void setCookie(String name, Cookie cookie) { this.cookies.put(name, cookie); } /** * cookies ??????? * * @return cookies */ public Map<String, Cookie> getCookies() { return cookies; } /** * body ??????? * * @return body */ public byte[] getBody() { return body; } /** * body ??????? * * @param body * ????? body */ public void setBody(byte[] body) { this.body = body; } /** * statusCode ??????? * * @return statusCode */ public int getStatusCode() { return statusCode; } /** * statusCode ??????? * * @param statusCode * ????? statusCode */ public void setStatusCode(int statusCode) { this.statusCode = statusCode; } /** * contentLength ??????? * * @return contentLength */ public long getContentLength() { return contentLength; } /** * contentLength ??????? * * @param contentLength * ????? contentLength */ public void setContentLength(long contentLength) { this.contentLength = contentLength; } /** * characterEncoding ??????? ????????? text/* ? charset ?????????????????? * * @return characterEncoding */ public String getCharacterEncoding() { return characterEncoding; } /** * characterEncoding ??????? * * @param characterEncoding * ????? characterEncoding */ public void setCharacterEncoding(String characterEncoding) { this.characterEncoding = characterEncoding; } } /** * ????????????????????????????? * * @author Teruhiko Kusunoki<<a * href="teru.kusu@gmail.com">teru.kusu@gmail.com</a>> * */ class StringUtils { /** * ?????????????????????????????? */ private static final Pattern BLANK_PATTERN = Pattern.compile("^\\s*$"); /** * ???????????????? * * @param str * ???????? * @return ?????null???true??????false */ public static boolean isEmpty(String str) { return (str == null || str.length() == 0); } /** * null???????????????????????????????????? * * @param str * ???????? * @return null???????????????????????????? true??????false? */ public static boolean isBlank(String str) { return (str == null || BLANK_PATTERN.matcher(str).matches()); } /** * ???????????????????????????????? * * @return ????????????????????????? */ public static String getDefaultEncoding() { return System.getProperty("file.encoding"); } /** * Base64??????????????????????????????????? ?: Mzg1OTQyMjczMA== => * 3859422730 => 0xE60A1E0A => 10.30.10.230 * * @param str * Base64???????????????? * @return ??????????? */ public static String base64toInetAddress(String str) { // TODO ?? throw new UnsupportedOperationException(); // String numStr = new String(Base64.decode(str, Base64.DEFAULT)); // int num = Integer.parseInt(numStr); // // String ipAddr = (num & 0xFF) + "." + (num >> 8 & 0xFF) + "." // + (num >> 16 & 0xFF) + "." + (num >> 24 & 0xFF); // // return ipAddr; } } /** * @author Teruhiko Kusunoki<<a * href="teru.kusu@gmail.com">teru.kusu@gmail.com</a>> * */ class Cookie { private String key; private String value; private String path; private Date expire; private String domain; private boolean secure = false; /** * Set-Cookie ??????????????????????????? ????????????? * * @param cookieHeader * Set-Cookie????? * @return ?????????? * @throws ParseException * ????????? */ public static Cookie parseCookie(String cookieHeader) { return parseCookie(cookieHeader, null); } /** * Set-Cookie ??????????????????????????? * * @param cookieHeader * Set-Cookie????? * @param encode * cookie???????. null????????????????? * @return ?????????? * @throws ParseException * ????????? */ public static Cookie parseCookie(String cookieHeader, String encode) { Cookie c = new Cookie(); for (String str : cookieHeader.split("\\s*;\\s*")) { int index = str.indexOf('='); if (index < 0) { if ("secure".equals(str.toLowerCase())) { c.setSecure(true); } continue; } String key = str.substring(0, index).toLowerCase(); String value = str.substring(index + 1); if ("expires".equals(key.toLowerCase())) { SimpleDateFormat sdf = new SimpleDateFormat( "EEE, d-MMM-yyyy HH:mm:ss ZZZ", Locale.US); Date date = null; try { date = sdf.parse(value); } catch (ParseException e) { throw new RuntimeException(e.getMessage(), e); } c.setExpire(date); } else if ("domain".equals(key.toLowerCase())) { c.setDomain(value); } else if ("path".equals(key.toLowerCase())) { c.setPath(value); } else { c.setKey(key); if (StringUtils.isBlank(encode)) { c.setValue(value); } else { try { c.setValue(URLDecoder.decode(value, encode)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } } } return c; } /** * key ??????? * * @return key */ public String getKey() { return key; } /** * key ??????? * * @param key * ????? key */ public void setKey(String key) { this.key = key; } /** * value ??????? * * @return value */ public String getValue() { return value; } /** * value ??????? * * @param value * ????? value */ public void setValue(String value) { this.value = value; } /** * path ??????? * * @return path */ public String getPath() { return path; } /** * path ??????? * * @param path * ????? path */ public void setPath(String path) { this.path = path; } /** * expire ??????? * * @return expire */ public Date getExpire() { return expire; } /** * expire ??????? * * @param expire * ????? expire */ public void setExpire(Date expire) { this.expire = expire; } /** * domain ??????? * * @return domain */ public String getDomain() { return domain; } /** * domain ??????? * * @param domain * ????? domain */ public void setDomain(String domain) { this.domain = domain; } /** * secure ??????? * * @return secure */ public boolean isSecure() { return secure; } /** * secure ??????? * * @param secure * ????? secure */ public void setSecure(boolean secure) { this.secure = secure; } }