如何使用DefaultHttpClient将数据写入OutputStream?

14

如何使用org.apache.http.impl.client.DefaultHttpClient获取OutputStream

我想要将一个长字符串写入输出流。

如果使用HttpURLConnection,你可以这样实现:

HttpURLConnection connection = (HttpURLConnection)url.openConnection();
OutputStream out = connection.getOutputStream();
Writer wout = new OutputStreamWriter(out);
writeXml(wout);

使用DefaultHttpClient是否有类似于我上面的方法?如何使用DefaultHttpClient而不是HttpURLConnectionOutputStream写入数据?

例如:

DefaultHttpClient client = new DefaultHttpClient();

OutputStream outstream = (get OutputStream somehow)
Writer wout = new OutputStreamWriter(out);

@KeithRandall,我已经编辑过了。希望现在清楚了。 - Fabii
请解释一下你想写什么。在你的两个例子中,getOutputStream() 返回一个流,用于提交 HTTP POST 请求的请求数据。 - Eugene Kuleshov
@EugeneKuleshov,我该如何使用org.apache.http.impl.client.DefaultHttpClient获取输出流? - Fabii
4个回答

31

我知道已经有另一个答案被接受了,只是为了记录下来,这是如何使用HttpClient在不使用中间内存缓冲的情况下编写内容的方法。

    AbstractHttpEntity entity = new AbstractHttpEntity() {

        public boolean isRepeatable() {
            return false;
        }

        public long getContentLength() {
            return -1;
        }

        public boolean isStreaming() {
            return false;
        }

        public InputStream getContent() throws IOException {
            // Should be implemented as well but is irrelevant for this case
            throw new UnsupportedOperationException();
        }

        public void writeTo(final OutputStream outstream) throws IOException {
            Writer writer = new OutputStreamWriter(outstream, "UTF-8");
            writeXml(writer);
            writer.flush();
        }

    };
    HttpPost request = new HttpPost(uri);
    request.setEntity(entity);

2
这个过程还有其他的例子吗?writeTo方法在哪里被调用? - HGPB
1
@Haraldo 当请求被执行时,HttpClient框架调用request.getEntity().writeTo()。这意味着您不能编写一个单线程客户端,启动POST然后将其OutputStream返回给调用者以调用多个写入操作。 - slim
@oleg,我的Android未定义writeXml()函数?是你写的吗?能否请你展示一下你的writeXml()代码? - Majid Golshadi
只是为了澄清一下,关于isStreaming() - 在这里返回false是否正确?我认为这种逻辑应该被视为流式处理。 - Hakanai
1
这个答案对于内存来说比那个正确答案更好。 - Slim_user71169
"...这意味着您无法编写一个单线程客户端,该客户端启动POST然后将其OutputStream返回给调用者以调用多个写操作。" - 如果您有这样的用例怎么办?看起来您别无选择,只能先刷新到磁盘或使用PipedStreams(多线程)。 - Robert Christian

16

你无法直接从BasicHttpClient获取OutputStream。你需要创建一个HttpUriRequest对象,并给它一个封装你想要发送的内容的HttpEntity。例如,如果你的输出足够小,可以放入内存中,那么可以这样做:

// Produce the output
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(out, "UTF-8");
writeXml(writer);

// Create the request
HttpPost request = new HttpPost(uri);
request.setEntity(new ByteArrayEntity(out.toByteArray()));

// Send the request
DefaultHttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(request);

如果数据很大,需要使用流传输,则会变得更加困难,因为没有接受 OutputStreamHttpEntity 实现。您需要写入一个临时文件并使用 FileEntity 或者可能设置一个管道并使用 InputStreamEntity

编辑 参见 oleg 的答案,其中提供了演示如何流式传输内容的示例代码 - 您根本不需要临时文件或管道。


问题:这个writeXml(writer)在做什么?另外,你是如何将字节数组传递给request.setEntity()的? - Matt Grogan
@MattGrogan 这是在实现应用逻辑以实际生成要POST到远程服务器的内容(从问题中的示例代码中复制而来)。 - Alex
那么,如果我有需要写入的数据,我是不是应该直接调用writer.write(data)来代替writeXml()呢? - Matt Grogan
是的,writeXml()正在计算一些内容以传递给writer.write()。 - Alex
抱歉有额外的问题,但我真的需要让这个工作起来。您是如何将 out.toByteArray() 传递给 request.setEntity() 的呢?我没有看到任何接受字节数组的重载。 - Matt Grogan
抱歉,应该将其包装在对 new ByteArrayEntity() 的调用中。请参见编辑后的示例代码。 - Alex

3

这个在安卓上很有效。对于大文件也应该可以正常工作,因为不需要缓冲。

PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream();
out.connect(in);
new Thread() {
    @Override
    public void run() {
        //create your http request
        InputStreamEntity entity = new InputStreamEntity(in, -1);
        request.setEntity(entity);
        client.execute(request,...);
        //When this line is reached your data is actually written
    }
}.start();
//do whatever you like with your outputstream.
out.write("Hallo".getBytes());
out.flush();
//close your streams

2
我翻译的内容是:

我编写了一个 Apache HTTP 客户端 API 框架的版本 [PipedApacheClientOutputStream],它提供了一个基于 Apache Commons HTTP Client 4.3.4 的 HTTP POST 流接口。

调用代码如下:

// Calling-code manages thread-pool
ExecutorService es = Executors.newCachedThreadPool(
  new ThreadFactoryBuilder()
  .setNameFormat("apache-client-executor-thread-%d")
  .build());


// Build configuration
PipedApacheClientOutputStreamConfig config = new      
  PipedApacheClientOutputStreamConfig();
config.setUrl("http://localhost:3000");
config.setPipeBufferSizeBytes(1024);
config.setThreadPool(es);
config.setHttpClient(HttpClientBuilder.create().build());

// Instantiate OutputStream
PipedApacheClientOutputStream os = new     
PipedApacheClientOutputStream(config);

// Write to OutputStream
os.write(...);

try {
  os.close();
} catch (IOException e) {
  logger.error(e.getLocalizedMessage(), e);
}

// Do stuff with HTTP response
...

// Close the HTTP response
os.getResponse().close();

// Finally, shut down thread pool
// This must occur after retrieving response (after is) if interested   
// in POST result
es.shutdown();

注意 - 在实践中,同一个客户端、执行器服务和配置可能会在应用程序的整个生命周期中被重复使用,因此上面示例中的外部准备和关闭代码很可能会存在于引导/初始化和完成代码中,而不是直接与OutputStream实例化内联。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接