代码之家  ›  专栏  ›  技术社区  ›  Martin

使用inputStreamResponseListener使用Jetty HTTP客户端复制inputStream块

  •  0
  • Martin  · 技术社区  · 6 年前

    我正在使用Jetty9.4.8HTTP客户机,希望将传入数据流写入文件。目前我正在使用一个inputstreamResponseListener和ioutils.copy(..)写入一个fileoutputstream。我还尝试了files.copy()。

    InputStreamResponseListener streamResponseListener = new InputStreamResponseListener();
    
    request.send(streamResponseListener);
    
    if(streamResponseListener.get(5, TimeUnit.MINUTES).getStatus() == 200) {
        OutputStream outputStream = null;
        try {
            TMP_FILE.toFile().createNewFile();
            outputStream = new FileOutputStream(TMP_FILE.toFile());
            IOUtils.copy(inputStream, outputStream);
        } catch(IOException e) {
            this.getLogService().log(..)
        } finally {
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(outputStream);
        }
    
        // NOT REACHED IN CASE InputStream is BLOCKED FOR SOME REASON
    }
    

    但是,复制方法似乎在收到所有字节后被阻塞。为什么会发生这种情况?我怎样才能避免这种情况?

    请求的HTTP内容的头:

    Date: Wed, 23 May 2018 16:46:06 GMT
    Content-Type: application/octet-stream
    Content-Disposition: attachment; filename=".."
    Content-Length: 613970044
    Server: Jetty(9.4.8.v20171121)
    

    来自Apache Commons IO 2.4版的ioutils

    1 回复  |  直到 6 年前
        1
  •  0
  •   Joakim Erdfelt    6 年前

    这是你的代码库的一个工作示例,只使用Java和JETTY。

    这是从已知符合HTTP规范的服务器请求内容。

    package demo.jettyclient;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URI;
    import java.net.URL;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.Properties;
    import java.util.concurrent.TimeUnit;
    
    import org.eclipse.jetty.client.HttpClient;
    import org.eclipse.jetty.client.api.Request;
    import org.eclipse.jetty.client.api.Response;
    import org.eclipse.jetty.client.util.InputStreamResponseListener;
    import org.eclipse.jetty.http.HttpStatus;
    import org.eclipse.jetty.util.IO;
    import org.eclipse.jetty.util.StringUtil;
    import org.eclipse.jetty.util.ssl.SslContextFactory;
    
    public class DownloadUrl
    {
        public static void main(String[] args) throws Exception
        {
            String uriString = "https://upload.wikimedia.org/wikipedia/en/a/a1/Jetty_logo.png?download";
    
            if (args.length >= 1)
                uriString = args[0];
    
            URI srcUri = URI.create(uriString);
    
            SslContextFactory ssl = new SslContextFactory(true);
    
            HttpClient client = new HttpClient(ssl);
            try
            {
                client.start();
    
                Request request = client.newRequest(srcUri);
    
                System.out.printf("Using HttpClient v%s%n", getHttpClientVersion());
                System.out.printf("Requesting: %s%n", srcUri);
                InputStreamResponseListener streamResponseListener = new InputStreamResponseListener();
                request.send(streamResponseListener);
                Response response = streamResponseListener.get(5, TimeUnit.SECONDS);
    
                if (response.getStatus() != HttpStatus.OK_200)
                {
                    throw new IOException(
                            String.format("Failed to GET URI [%d %s]: %s",
                                    response.getStatus(),
                                    response.getReason(),
                                    srcUri));
                }
    
                Path tmpFile = Files.createTempFile("tmp", ".dl");
    
                try (InputStream inputStream = streamResponseListener.getInputStream();
                     OutputStream outputStream = Files.newOutputStream(tmpFile))
                {
                    IO.copy(inputStream, outputStream);
                }
    
                System.out.printf("Downloaded %s%n", srcUri);
                System.out.printf("Destination: %s (%,d bytes)%n", tmpFile.toString(), Files.size(tmpFile));
            }
            finally
            {
                client.stop();
            }
        }
    
        private static String getHttpClientVersion()
        {
            ClassLoader cl = HttpClient.class.getClassLoader();
    
            // Attempt to use maven pom properties first
            String pomResource = "/META-INF/maven/org/eclipse/jetty/jetty-client/pom.properties";
            URL url = cl.getResource(pomResource);
            if (url != null)
            {
                try (InputStream in = url.openStream())
                {
                    Properties props = new Properties();
                    props.load(in);
                    String version = props.getProperty("version");
                    if (StringUtil.isNotBlank(version))
                        return version;
                }
                catch (IOException ignore)
                {
                }
            }
    
            // Attempt to use META-INF/MANIFEST.MF
            String version = HttpClient.class.getPackage().getImplementationVersion();
            if (StringUtil.isNotBlank(version))
                return version;
    
            return "<unknown>";
        }
    }
    

    当运行时,这会导致…

    2018-05-23 10:52:08.401:INFO::main: Logging initialized @325ms to org.eclipse.jetty.util.log.StdErrLog
    Using HttpClient v9.4.9.v20180320
    Requesting: https://upload.wikimedia.org/wikipedia/en/a/a1/Jetty_logo.png?download
    Downloaded https://upload.wikimedia.org/wikipedia/en/a/a1/Jetty_logo.png?download
    Destination: C:\Users\joakim\AppData\Local\Temp\tmp2166600286896937563.dl (11,604 bytes)
    
    Process finished with exit code 0
    

    以下一个(或多个)可能导致您的问题。

    1. 服务器有问题,不符合HTTP规范。
    2. HTTP交换还没有完成(从协议的角度来看)。捕获流量并验证行为。
    3. 您正在使用的ioutil库(您没有说是哪个库)有一个bug。

    wget(或curl)工作的事实很可能是因为它们对内容长度不严格(根据RFC7230中的建议),并且将显示/下载接收到的所有内容,直到物理连接断开/断开。虽然HTTP/1.1协议具有连接持久性,并且在请求(和响应)内容结束时有严格的规则。