Java URL、URLConnection和URLPermission

URL

        URL类表示统一资源定位符,即指向万维网上“资源”的指针。 资源可以是简单的文件或目录,也可以是对更复杂对象的引用,例如对数据库或搜索引擎的查询。

protocol://host:port/file

举例:http://news.baidu.com:80/guonei

URL类提供了多个构造器用于创建URL对象:

URL(String protocol, String host, int port, String file)
URL(String protocol, String host, String file)
URL(String protocol, String host, int port, String file, URLStreamHandler handler)

注意:

        抽象类URLStreamHandler是所有流协议处理程序的通用超类。 流协议处理程序知道如何为特定的协议类型建立连接,例如http或https 。
        在大多数情况下, URLStreamHandler子类的实例不是由应用程序直接创建的。 相反,在构造URL时第一次遇到协议名称时,会自动加载适当的流协议处理程序。

URL(String spec):参数:spec – 要解析为 URL 的String 。
URL(URL context, String spec):context — 解析规范的上下文。
URL(URL context, String spec, URLStreamHandler handler)

访问URL对象对应的资源的常用方法:

➢ String getFile():获取该URL的资源名。
(返回的文件部分将与getPath()相同,如果getQuery()有值则串联getQuery()的值,如果没有查询部分,此方法和getPath()将返回相同的结果。)

➢ String getHost():获取该URL的主机名。

➢ String getPath():获取该URL的路径部分。

➢ int getPort():获取该URL的端口号。

➢ String getProtocol():获取该URL的协议名称。

➢ String getQuery():获取该URL的查询字符串部分。

➢ URLConnection openConnection():返回一个URLConnection对象,它代表了与URL所引用的远程对象的连接。

➢ URLConnection openConnection(Proxy proxy):与openConnection()相同,除了连接将通过指定的代理进行;不支持代理的协议处理程序将忽略代理参数并进行正常连接。 调用此方法会抢占系统的默认 ProxySelector 设置。

➢ InputStream openStream():打开与此URL的连接,并返回一个用于读取该URL资源的InputStream。

通过URL对象的openStream()方法来读取URL资源的InputStream,来实现多线程下载。

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;

/**
 * @program: Download
 * @description: 多线程下载工具类
 * @author: YOUNG
 * @create: 2021-09-29 16:56
 */
public class Download {
    /**
     * url
     */
    private URL url;
    /**
     * 线程数量
     */
    private int threadCount;
    /**
     * 文件名
     */
    private String filename = UUID.randomUUID().toString();

    public static void main(String[] args) {
        new Download().load("下载链接", "文件名").setThreadCount(8).start();
    }

    public Download load(String url, String filename) {
        try {
            this.url = new URL(url);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        if (this.url.getProtocol() == null
                || this.url.getHost() == null
                || this.url.getFile() == null)
            throw new RuntimeException("URL地址不正确!");
        this.filename = filename;
        return this;
    }

    public Download setThreadCount(int threadCount) {
        if (threadCount < 0)
            this.threadCount = 1;
        else this.threadCount = Math.min(threadCount, Runtime.getRuntime().availableProcessors());
        return this;
    }


    public void start() {
        try {
            URLConnection urlConnection = url.openConnection();
            long fileLength = urlConnection.getContentLength();
            System.out.println("下载文件的大小:" + fileLength);
            RandomAccessFile file = new RandomAccessFile(filename, "rwd");
            file.setLength(fileLength);
            file.close();
            long threadBlockSize = fileLength / threadCount;
            long finalThreadSize = fileLength % threadCount;
            System.out.println("分块大小:" + threadBlockSize);
            System.out.println("最后一块大小:" + (threadBlockSize + finalThreadSize));
            for (int i = 0; i < threadCount; i++) {
                if (i == threadCount - 1)
                    new Thread(new threadBlock(i, threadBlockSize * i, threadBlockSize + finalThreadSize)).start();
                else
                    new Thread(new threadBlock(i, threadBlockSize * i, threadBlockSize)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    class threadBlock implements Runnable {
        private final long skipSize;
        private int threadId;
        private long downloadSize;


        public threadBlock(int threadId, long skipSize, long downloadSize) {
            this.threadId = threadId;
            this.skipSize = skipSize;
            this.downloadSize = downloadSize;
        }

        public threadBlock(long skipSize, long downloadSize) {
            this.skipSize = skipSize;
            this.downloadSize = downloadSize;
        }

        @Override
        public void run() {
            try (InputStream inputStream = url.openStream();
                 RandomAccessFile tmpFile = new RandomAccessFile(filename, "rw")) {
                inputStream.skip(skipSize);
                tmpFile.seek(skipSize);

                System.out.println(threadId + "-跳过:" + skipSize);
                //1kB
                byte[] buff = new byte[1024];
                System.out.println(threadId + "-开始:" + tmpFile.getFilePointer());
                int readLen = 0;
                while (downloadSize > 0) {
                    readLen = inputStream.read(buff);
                    if (readLen == -1)
                        break;
                    tmpFile.write(buff, 0, readLen);
                    downloadSize -= readLen;
                    System.out.println(readLen);
                    System.out.println(threadId + "-当前:" + tmpFile.getFilePointer());
                }
                System.out.println(threadId + "-end:" + tmpFile.getFilePointer());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

URLConnection

通常创建一个和URL的连接,并发送请求、读取此URL引用的资源需要如下几个步骤:

① 通过调用URL对象的openConnection()方法来创建URLConnection对象。
② 设置URLConnection的参数和普通请求属性。
③ 如果只是发送GET方式请求,则使用connect()方法建立和远程资源之间的实际连接即可;如果需要发送POST方式的请求,则需要获取URLConnection实例对应的输出流来发送请求参数。
④ 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据。

在建立和远程资源的实际连接之前,程序可以通过如下方法来设置请求头字段。

➢ setAllowUserInteraction():设置该URLConnection的allowUserInteraction请求头字段的值。

➢ setDoInput():设置该URLConnection的doInput请求头字段的值。

➢ setDoOutput():设置该URLConnection的doOutput请求头字段的值。

➢ setIfModifiedSince():设置该URLConnection的ifModifiedSince请求头字段的值。

➢ setUseCaches():设置该URLConnection的useCaches请求头字段的值。

setRequestProperty(String key, String value):设置该URLConnection的key请求头字段的值为value。

addRequestProperty(String key, String value):为该URLConnection的key请求头字段增加value值,该方法并不会覆盖原请求头字段的值,而是将新值追加到原请求头字段中。

当远程资源可用之后,程序可以使用以下方法来访问头字段和内容。

➢ Object getContent():获取该URLConnection的内容。

➢ String getHeaderField(String name):获取指定响应头字段的值。

  • getContentEncoding():获取content-encoding响应头字段的值。
  • getContentLength():获取content-length响应头字段的值。
  • getContentType():获取content-type响应头字段的值。
  • getDate():获取date响应头字段的值。
  • getExpiration():获取expires响应头字段的值。
  • getLastModified():获取last-modified响应头字段的值。


➢ getInputStream():返回该URLConnection对应的输入流,用于获取URLConnection响应的内容。

➢ getOutputStream():返回该URLConnection对应的输出流,用于向URLConnection发送请求参数。

注意:
        如果既要使用输入流读取URLConnection响应的内容,又要使用输出流发送请求参数,则一定要先使用输出流,再使用输入流

    /**
     * 向指定URL发送GET方法的请求
     *
     * @param url   发送请求的URL
     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
     * @return URL所代表远程资源的响应
     */
    public static String get(String url, String param) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlName = url + "?" + param;
            URL realUrl = new URL(urlName);
            //打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            //设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            //建立实际的连接
            conn.connect();
            //获取所有响应头字段
            Map<String, List<String>> map = conn.getHeaderFields();
            System.out.println(map);
            //定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append("\n").append(line);
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        //使用finally块来关闭输入流
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result.toString();
    }

    /**
     * 向指定URL发送POST方法的请求
     *
     * @param url   发送请求的URL
     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
     * @return URL所代表远程资源的响应
     */
    public static String post(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            URL realUrl = new URL(url);
            //打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            //设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            //发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            //发送请求参数
            out.print(param);
            //flush输出流的缓冲
            out.flush();
            //定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append("\n").append(line);
            }
        } catch (Exception e) {
            System.out.println("发送POST请求出现异常!" + e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result.toString();
    }

URLPermission 

        Java 8 新增了一个 URLPermission 工具类,用于管理 HttpURLConnection 的权限问题,如果在 HttpURLConnection 安装了安全管理器,通过该对象打开连接时就需要先获得权限。

猜你喜欢

转载自blog.csdn.net/qq_40100414/article/details/120549866
今日推荐