来自 URL 的 InputStream

Posted

技术标签:

【中文标题】来自 URL 的 InputStream【英文标题】:InputStream from a URL 【发布时间】:2011-10-19 10:18:10 【问题描述】:

如何从 URL 获取 InputStream?

例如,我想在 url wwww.somewebsite.com/a.txt 处获取文件,并通过 servlet 将其作为 Java 中的 InputStream 读取。

我试过了

InputStream is = new FileInputStream("wwww.somewebsite.com/a.txt");

但我得到的是一个错误:

java.io.FileNotFoundException

【问题讨论】:

你为什么回滚删除servlets标签?这里不涉及javax.servlet.* API。在使用 main() 方法的普通 Java 类中这样做时,您会遇到完全相同的问题。 或许您应该熟悉一下什么是 URL:docs.oracle.com/javase/tutorial/networking/urls/definition.html 【参考方案1】:

使用 java.net.URL#openStream() 和正确的 URL(包括协议!)。例如

InputStream input = new URL("http://www.somewebsite.com/a.txt").openStream();
// ...

另见:

Using java.net.URLConnection to fire and handle HTTP requests

【讨论】:

您是否知道这是否会在每次读取 InputStream 时发出网络请求,或者是否一次读取整个文件以便不必在读取时发出网络请求? android 的 UI 线程中调用此方法会引发异常。在后台线程中执行此操作。使用Bolts-Android【参考方案2】:

试试:

final InputStream is = new URL("http://wwww.somewebsite.com/a.txt").openStream();

【讨论】:

【参考方案3】:

(a) wwww.somewebsite.com/a.txt 不是“文件 URL”。它根本不是一个 URL。如果您将http:// 放在它的前面,它将是一个HTTP URL,这显然是您想要的。

(b)FileInputStream 用于文件,而不是 URL。

(c) 从any URL 获取输入流的方法是通过URL.openStream(),URL.getConnection().getInputStream(),,这是等效的,但您可能有其他理由获取URLConnection 并使用先说吧。

【讨论】:

【参考方案4】:

您的原始代码使用 FileInputStream,用于访问文件系统托管文件。

您使用的构造函数将尝试在当前工作目录的 www.somewebsite.com 子文件夹中找到名为 a.txt 的文件(系统属性 user.dir 的值)。您提供的名称将使用 File 类解析为文件。

URL 对象是解决这个问题的通用方法。您可以使用 URL 访问本地文件,也可以使用网络托管资源。 URL 类除了 http:// 或 https:// 之外还支持 file:// 协议,所以你很高兴。

【讨论】:

【参考方案5】:

纯Java:

 urlToInputStream(url,httpHeaders);

我使用这种方法取得了一些成功。它处理重定向,并且可以将可变数量的 HTTP 标头 传递为Map<String,String>。它还允许从 HTTP 重定向到 HTTPS

private InputStream urlToInputStream(URL url, Map<String, String> args) 
    HttpURLConnection con = null;
    InputStream inputStream = null;
    try 
        con = (HttpURLConnection) url.openConnection();
        con.setConnectTimeout(15000);
        con.setReadTimeout(15000);
        if (args != null) 
            for (Entry<String, String> e : args.entrySet()) 
                con.setRequestProperty(e.getKey(), e.getValue());
            
        
        con.connect();
        int responseCode = con.getResponseCode();
        /* By default the connection will follow redirects. The following
         * block is only entered if the implementation of HttpURLConnection
         * does not perform the redirect. The exact behavior depends to 
         * the actual implementation (e.g. sun.net).
         * !!! Attention: This block allows the connection to 
         * switch protocols (e.g. HTTP to HTTPS), which is <b>not</b> 
         * default behavior. See: https://***.com/questions/1884230 
         * for more info!!!
         */
        if (responseCode < 400 && responseCode > 299) 
            String redirectUrl = con.getHeaderField("Location");
            try 
                URL newUrl = new URL(redirectUrl);
                return urlToInputStream(newUrl, args);
             catch (MalformedURLException e) 
                URL newUrl = new URL(url.getProtocol() + "://" + url.getHost() + redirectUrl);
                return urlToInputStream(newUrl, args);
            
        
        /*!!!!!*/

        inputStream = con.getInputStream();
        return inputStream;
     catch (Exception e) 
        throw new RuntimeException(e);
    

完整的示例调用

private InputStream getInputStreamFromUrl(URL url, String user, String passwd) throws IOException 
        String encoded = Base64.getEncoder().encodeToString((user + ":" + passwd).getBytes(StandardCharsets.UTF_8));
        Map<String,String> httpHeaders=new Map<>();
        httpHeaders.put("Accept", "application/json");
        httpHeaders.put("User-Agent", "myApplication");
        httpHeaders.put("Authorization", "Basic " + encoded);
        return urlToInputStream(url,httpHeaders);
    

【讨论】:

HttpURLConnection 已经遵循重定向,除非你告诉它不要这样做,而你没有这样做。 我知道 OP 没有提到标题,但我很欣赏这个简洁(好吧,考虑到它是 Java)的例子。 @EJP 我添加了一些解释作为内联注释。我想,我主要介绍了 HTTP 301 将 HTTP 地址重定向到 HTTPS 地址的情况下的重定向块。当然,这超出了最初的问题,而是默认实现不处理的常见用例。见:***.com/questions/1884230/… 您的代码在没有重定向块的情况下同样有效,因为HttpURLConnection 默认情况下已经遵循重定向,正如我已经说过的那样。 @user207421 这部分正确。重定向块用于协议切换,例如 http->https,默认情况下不支持。我试图在代码注释中表达这一点。见***.com/questions/1884230/…。【参考方案6】:

这是一个读取给定网页内容的完整示例。 该网页是从 HTML 表单中读取的。我们使用标准的InputStream 类,但使用 JSoup 库可以更轻松地完成。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>

</dependency>

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.6</version>
</dependency>  

这些是 Maven 依赖项。我们使用 Apache Commons 库来验证 URL 字符串。

package com.zetcode.web;

import com.zetcode.service.WebPageReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "ReadWebPage", urlPatterns = "/ReadWebPage")
public class ReadWebpage extends HttpServlet 

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        response.setContentType("text/plain;charset=UTF-8");

        String page = request.getParameter("webpage");

        String content = new WebPageReader().setWebPageName(page).getWebPageContent();

        ServletOutputStream os = response.getOutputStream();
        os.write(content.getBytes(StandardCharsets.UTF_8));
    

ReadWebPage servlet 读取给定网页的内容并以纯文本格式将其发送回客户端。阅读页面的任务委托给WebPageReader

package com.zetcode.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.apache.commons.validator.routines.UrlValidator;

public class WebPageReader 

    private String webpage;
    private String content;

    public WebPageReader setWebPageName(String name) 

        webpage = name;
        return this;
    

    public String getWebPageContent() 

        try 

            boolean valid = validateUrl(webpage);

            if (!valid) 

                content = "Invalid URL; use http(s)://www.example.com format";
                return content;
            

            URL url = new URL(webpage);

            try (InputStream is = url.openStream();
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(is, StandardCharsets.UTF_8))) 

                content = br.lines().collect(
                      Collectors.joining(System.lineSeparator()));
            

         catch (IOException ex) 

            content = String.format("Cannot read webpage %s", ex);
            Logger.getLogger(WebPageReader.class.getName()).log(Level.SEVERE, null, ex);
        

        return content;
    

    private boolean validateUrl(String webpage) 

        UrlValidator urlValidator = new UrlValidator();

        return urlValidator.isValid(webpage);
    

WebPageReader 验证 URL 并读取网页内容。 它返回一个包含页面 HTML 代码的字符串。

<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <form action="ReadWebPage">

            <label for="page">Enter a web page name:</label>
            <input  type="text" id="page" name="webpage">

            <button type="submit">Submit</button>

        </form>
    </body>
</html>

最后,这是包含 HTML 表单的主页。 这取自我的tutorial 关于这个话题。

【讨论】:

以上是关于来自 URL 的 InputStream的主要内容,如果未能解决你的问题,请参考以下文章

无法在android的videoview中播放来自url的视频如何在videoview中播放来自URL的视频?

来自 C# 中的基本 URL + 相对 URL 的绝对 URL

来自 URL 的正则表达式 URL 路径

来自外部 url 的 wordpress 特色图片,无需下载

不重定向来自移动设备的某些 URL [重复]

来自 Objective-C 中 URL 的数组