InputStream de um URL

117

Como obtenho um InputStream de um URL?

por exemplo, quero pegar o arquivo no url wwww.somewebsite.com/a.txte lê-lo como um InputStream em Java, por meio de um servlet.

eu tentei

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

mas o que recebi foi um erro:

java.io.FileNotFoundException
Urso polar
fonte
1
Por que você reverteu a remoção da servletstag? Não há javax.servlet.*API envolvida aqui. Você teria exatamente o mesmo problema ao fazer isso em uma classe Java simples com um main()método.
BalusC
1
Talvez você deva se familiarizar com o que é um URL: docs.oracle.com/javase/tutorial/networking/urls/definition.html
b1nary.atr0phy

Respostas:

228

Use java.net.URL#openStream()com um URL adequado (incluindo o protocolo!). Por exemplo

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

Veja também:

BalusC
fonte
2
Você sabe se isso faz uma solicitação de rede em cada leitura do InputStream ou se ele lê o arquivo inteiro de uma vez para não ter que fazer solicitações de rede nas leituras?
gsingh2011
Chamar esse método no thread de IU no Android gerará uma exceção. Faça isso em um tópico de fundo. Use Bolts-Android
Behrouz.M
19

Experimentar:

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

(a) wwww.somewebsite.com/a.txtnão é um 'URL de arquivo'. Não é um URL. Se você colocasse http://na frente, seria uma URL HTTP, que é claramente o que você pretende aqui.

(b) FileInputStreamé para arquivos, não URLs.

(c) A maneira de obter um fluxo de entrada de qualquer URL é via URL.openStream(),ou URL.getConnection().getInputStream(),equivalente, mas você pode ter outros motivos para obter URLConnectione brincar com ele primeiro.

Marquês de Lorne
fonte
4

Seu código original usa FileInputStream, que é para acessar arquivos hospedados do sistema de arquivos.

O construtor usado tentará localizar um arquivo denominado a.txt na subpasta www.somewebsite.com do diretório de trabalho atual (o valor da propriedade do sistema user.dir). O nome fornecido é resolvido para um arquivo usando a classe File.

Objetos de URL são a maneira genérica de resolver isso. Você pode usar URLs para acessar arquivos locais, mas também recursos hospedados na rede. A classe URL suporta o protocolo file: // além de http: // ou https: //, então você está pronto para começar.

Cristian Botiza
fonte
2

Java puro:

 urlToInputStream(url,httpHeaders);

Com algum sucesso, uso esse método. Ele lida com redirecionamentos e pode-se passar um número variável de cabeçalhos HTTP como Map<String,String>. Ele também permite redirecionamentos de HTTP para 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: /programming/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);
    }
}

Exemplo completo de chamada

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);
    }
jschnasse
fonte
HttpURLConnectionjá seguirá os redirecionamentos, a menos que você diga para não fazer isso, o que você não fez.
Marquês de Lorne
1
Sei que o OP não mencionou cabeçalhos, mas agradeço o exemplo sucinto (bem, considerando que é Java).
chbrown
@EJP Eu adicionei alguma explicação como comentário embutido. Acho que apresentei principalmente o bloco de redirecionamento para o caso em que o HTTP 301 redireciona um endereço HTTP para um endereço HTTPS. Claro, isso vai além da questão original, mas é um caso de uso comum que não é tratado pela implementação padrão. Consulte: stackoverflow.com/questions/1884230/…
jschnasse
Seu código funciona igualmente bem sem o bloco de redirecionamento, pois HttpURLConnectionjá segue redirecionamentos por padrão, como já disse.
Marquês de Lorne
@ user207421 Isso está parcialmente correto. O bloco de redirecionamento é para switches de protocolo como http-> https, que não é compatível por padrão. Tentei expressar isso no comentário no código. Consulte stackoverflow.com/questions/1884230/… .
jschnasse
-1

Aqui está um exemplo completo que lê o conteúdo de uma determinada página da web. A página da web é lida a partir de um formulário HTML. Usamos InputStreamclasses padrão , mas isso poderia ser feito mais facilmente com a biblioteca 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>  

Essas são as dependências do Maven. Usamos a biblioteca Apache Commons para validar strings de 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));
    }
}

O ReadWebPageservlet lê o conteúdo de determinada página da web e o envia de volta ao cliente em formato de texto simples. A tarefa de ler a página é delegada 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);
    }
}

WebPageReadervalida o URL e lê o conteúdo da página da web. Ele retorna uma string contendo o código HTML da página.

<!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>

Finalmente, esta é a página inicial que contém o formulário HTML. Isso foi tirado do meu tutorial sobre este tópico.

Jan Bodnar
fonte