InputStream desde una URL

117

¿Cómo obtengo un InputStream de una URL?

por ejemplo, quiero tomar el archivo en la URL wwww.somewebsite.com/a.txty leerlo como InputStream en Java, a través de un servlet.

He intentado

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

pero lo que obtuve fue un error:

java.io.FileNotFoundException
Oso blanco
fuente
1
¿Por qué revertiste la eliminación de la servletsetiqueta? No hay javax.servlet.*API involucrada aquí. Tendría exactamente el mismo problema al hacerlo en una clase Java simple con un main()método.
BalusC
1
Quizás debería familiarizarse con lo que es una URL: docs.oracle.com/javase/tutorial/networking/urls/definition.html
b1nary.atr0phy

Respuestas:

228

Úselo java.net.URL#openStream()con una URL adecuada (¡incluido el protocolo!). P.ej

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

Ver también:

BalusC
fuente
2
¿Sabe si esto hace una solicitud de red en cada lectura de InputStream o si lee todo el archivo a la vez para no tener que realizar solicitudes de red en las lecturas?
gsingh2011
Llamar a este método en el hilo de la interfaz de usuario en Android generará una excepción. Hágalo en un hilo de fondo. Use Bolts-Android
Behrouz.M
19

Tratar:

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

(a) wwww.somewebsite.com/a.txtno es una 'URL de archivo'. No es una URL en absoluto. Si lo coloca http://al frente, sería una URL HTTP, que es claramente lo que pretende aquí.

(b) FileInputStreames para archivos, no URL.

(c) La forma de obtener un flujo de entrada desde cualquier URL es a través de URL.openStream(),o URL.getConnection().getInputStream(),que es equivalente, pero es posible que tenga otras razones para obtener el URLConnectiony jugar con él primero.

Marqués de Lorne
fuente
4

Su código original usa FileInputStream, que es para acceder a archivos alojados en el sistema de archivos.

El constructor que usó intentará ubicar un archivo llamado a.txt en la subcarpeta www.somewebsite.com del directorio de trabajo actual (el valor de la propiedad del sistema user.dir). El nombre que proporcione se resuelve en un archivo mediante la clase Archivo.

Los objetos URL son la forma genérica de resolver esto. Puede utilizar URL para acceder a archivos locales, pero también a recursos alojados en la red. La clase de URL admite el protocolo file: // además de http: // o https: //, por lo que está listo para comenzar.

Cristian Botiza
fuente
2

Java puro:

 urlToInputStream(url,httpHeaders);

Utilizo este método con cierto éxito. Se maneja redirecciones y uno puede pasar un número variable de cabeceras HTTP como Map<String,String>. También permite redireccionamientos de HTTP a 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);
    }
}

Llamada de ejemplo completa

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
fuente
HttpURLConnectionya seguirá las redirecciones a menos que usted le diga que no lo haga, lo que no ha hecho.
Marqués de Lorne
1
Sé que OP no mencionó los encabezados, pero aprecio el ejemplo sucinto (bueno, considerando que es Java).
chbrown
@EJP Agregué una explicación como comentario en línea. Creo que introduje principalmente el bloque de redireccionamiento para el caso en que HTTP 301 redirige una dirección HTTP a una dirección HTTPS. Por supuesto, esto va más allá de la pregunta original, pero es un caso de uso común que no se maneja mediante la implementación predeterminada. Ver: stackoverflow.com/questions/1884230/…
jschnasse
Su código funciona igualmente bien sin el bloque de redireccionamiento, como HttpURLConnectionya sigue los redireccionamientos por defecto, como ya dije.
Marqués de Lorne
@ user207421 Esto es parcialmente correcto. El bloque de redireccionamiento es para conmutadores de protocolo como http-> https que no es compatible de forma predeterminada. Traté de expresar eso en el comentario en el código. Consulte stackoverflow.com/questions/1884230/… .
jschnasse
-1

Aquí hay un ejemplo completo que lee el contenido de la página web dada. La página web se lee desde un formulario HTML. Usamos InputStreamclases estándar , pero podría hacerse más fácilmente con la 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>  

Estas son las dependencias de Maven. Usamos la biblioteca Apache Commons para validar cadenas 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));
    }
}

El ReadWebPageservlet lee el contenido de la página web dada y lo envía de vuelta al cliente en formato de texto sin formato. La tarea de leer la página está delegada a 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 la URL y lee el contenido de la página web. Devuelve una cadena que contiene el código HTML de la 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 es la página de inicio que contiene el formulario HTML. Esto está tomado de mi tutorial sobre este tema.

Jan Bodnar
fuente