InputStream z adresu URL

117

Jak uzyskać InputStream z adresu URL?

na przykład chcę pobrać plik pod adresem URL wwww.somewebsite.com/a.txti odczytać go jako strumień wejściowy w Javie za pośrednictwem serwletu.

próbowałem

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

ale otrzymałem błąd:

java.io.FileNotFoundException
Biały niedźwiedź
źródło
1
Dlaczego wycofałeś usunięcie servletstagu? Nie ma javax.servlet.*tu żadnego API. Dokładnie ten sam problem miałbyś, robiąc to w zwykłej, waniliowej klasie Java z main()metodą.
BalusC,
1
Być może powinieneś zapoznać się z tym, czym jest adres URL: docs.oracle.com/javase/tutorial/networking/urls/definition.html
b1nary.atr0phy,

Odpowiedzi:

228

Używaj java.net.URL#openStream()z odpowiednim adresem URL (łącznie z protokołem!). Na przykład

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

Zobacz też:

BalusC
źródło
2
Czy wiesz, czy powoduje to żądanie sieciowe przy każdym odczycie InputStream, czy też odczytuje cały plik naraz, więc nie musi wysyłać żądań sieciowych przy odczytach?
gsingh2011,
Wywołanie tej metody w wątku interfejsu użytkownika w systemie Android spowoduje zgłoszenie wyjątku. Zrób to w wątku w tle. Użyj Bolts-Android
Behrouz.
19

Próbować:

final InputStream is = new URL("http://wwww.somewebsite.com/a.txt").openStream();
whiskeysierra
źródło
10

(a) wwww.somewebsite.com/a.txtnie jest „adresem URL pliku”. To wcale nie jest adres URL. Jeśli umieścisz http://na początku, będzie to adres URL HTTP, co wyraźnie jest tym, co zamierzasz tutaj.

(b) FileInputStreamdotyczy plików, a nie adresów URL.

(c) Sposobem na uzyskanie strumienia wejściowego z dowolnego adresu URL jest użycie tego samego URL.openStream(),lub URL.getConnection().getInputStream(),równoważnego, ale możesz mieć inne powody, aby URLConnectionnajpierw pobrać plik i pobawić się nim.

Markiz Lorne
źródło
4

Twój oryginalny kod używa FileInputStream, który służy do uzyskiwania dostępu do plików hostowanych w systemie plików.

Konstruktor, którego użyłeś, spróbuje zlokalizować plik o nazwie a.txt w podfolderze www.somewebsite.com bieżącego katalogu roboczego (wartość właściwości systemowej user.dir). Podana nazwa jest tłumaczona na plik przy użyciu klasy File.

Obiekty URL są ogólnym sposobem rozwiązania tego problemu. Możesz użyć adresów URL, aby uzyskać dostęp do plików lokalnych, ale także zasobów hostowanych w sieci. Klasa URL obsługuje protokół file: // oprócz http: // lub https: //, więc nie ma problemu.

Cristian Botiza
źródło
2

Czysta Java:

 urlToInputStream(url,httpHeaders);

Z pewnym sukcesem używam tej metody. To obsługuje przekierowania i można przekazać zmienną liczbę nagłówków HTTP jako Map<String,String>. Umożliwia także przekierowania z HTTP do 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);
    }
}

Pełne przykładowe wezwanie

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
źródło
HttpURLConnectionbędzie już podążał za przekierowaniami, chyba że powiesz mu, aby tego nie robił, czego nie zrobiłeś.
Markiz Lorne
1
Wiem, że OP nie wspomniał o nagłówkach, ale doceniam zwięzły (cóż, biorąc pod uwagę język Java).
brązowy
@EJP Dodałem wyjaśnienie jako komentarz w tekście. Myślę, że głównie wprowadziłem blokadę przekierowań dla przypadku, gdy HTTP 301 przekierowuje adres HTTP na adres HTTPS. Oczywiście wykracza to poza pierwotne pytanie, ale jest to typowy przypadek użycia, który nie jest obsługiwany w domyślnej implementacji. Zobacz: stackoverflow.com/questions/1884230/…
jschnasse
Twój kod działa równie dobrze bez bloku przekierowań, ponieważ HttpURLConnectionjuż domyślnie następuje przekierowanie, jak już powiedziałem.
Markiz Lorne
@ user207421 To jest częściowo poprawne. Blok przekierowania dotyczy przełączników protokołów, takich jak http-> https, które nie są domyślnie obsługiwane. Próbowałem to wyrazić w komentarzu w kodzie. Zobacz stackoverflow.com/questions/1884230/… .
jschnasse
-1

Oto pełny przykład odczytujący zawartość podanej strony internetowej. Strona internetowa jest odczytywana z formularza HTML. Używamy InputStreamklas standardowych , ale byłoby to łatwiejsze dzięki bibliotece 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>  

To są zależności Mavena. Używamy biblioteki Apache Commons do weryfikacji ciągów adresów 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));
    }
}

ReadWebPageAplet odczytuje zawartość danej strony internetowej i wysyła go z powrotem do klienta w formacie tekstowym. Zadanie czytania strony jest delegowane 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);
    }
}

WebPageReadersprawdza poprawność adresu URL i odczytuje zawartość strony internetowej. Zwraca ciąg znaków zawierający kod HTML strony.

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

Wreszcie jest to strona główna zawierająca formularz HTML. To pochodzi z mojego tutoriala na ten temat.

Jan Bodnar
źródło