当前位置: 首页 > 知识库问答 >
问题:

OKHTTP 3跟踪多部分上载进度

芮瑾瑜
2023-03-14
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("title", "Square Logo")
            .addFormDataPart("image", "logo-square.png",
                    RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
            .build();

    Request request = new Request.Builder()
            .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
            .url("https://api.imgur.com/3/image")
            .post(requestBody)
            .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

共有1个答案

东门俊民
2023-03-14

我无法得到任何对我有效的答案。问题是,在图像上传之前,进度将运行到100%,暗示在数据通过网络发送之前,某个缓冲区已经被填满。经过一些研究,我发现确实是这样,这个缓冲区就是套接字发送缓冲区。向OkHttpClient提供一个SocketFactory终于奏效了。我的科特林密码如下...

首先,和其他人一样,我有一个CountingRequestBody,用于包装MultipartBody。

class CountingRequestBody(var delegate: RequestBody, private var listener: (max: Long, value: Long) -> Unit): RequestBody() {

    override fun contentType(): MediaType? {
        return delegate.contentType()
    }

    override fun contentLength(): Long {
        try {
            return delegate.contentLength()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return -1
    }

    override fun writeTo(sink: BufferedSink) {
        val countingSink = CountingSink(sink)
        val bufferedSink = Okio.buffer(countingSink)
        delegate.writeTo(bufferedSink)
        bufferedSink.flush()
    }

    inner class CountingSink(delegate: Sink): ForwardingSink(delegate) {
        private var bytesWritten: Long = 0

        override fun write(source: Buffer, byteCount: Long) {
            super.write(source, byteCount)
            bytesWritten += byteCount
            listener(contentLength(), bytesWritten)
        }
    }
}

我在Retrofit2中使用这个。一般用法如下:

val builder = MultipartBody.Builder()
// Add stuff to the MultipartBody via the Builder

val body = CountingRequestBody(builder.build()) { max, value ->
      // Progress your progress, or send it somewhere else.
}
class ProgressFriendlySocketFactory(private val sendBufferSize: Int = DEFAULT_BUFFER_SIZE) : SocketFactory() {

    override fun createSocket(): Socket {
        return setSendBufferSize(Socket())
    }

    override fun createSocket(host: String, port: Int): Socket {
        return setSendBufferSize(Socket(host, port))
    }

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
        return setSendBufferSize(Socket(host, port, localHost, localPort))
    }

    override fun createSocket(host: InetAddress, port: Int): Socket {
        return setSendBufferSize(Socket(host, port))
    }

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
        return setSendBufferSize(Socket(address, port, localAddress, localPort))
    }

    private fun setSendBufferSize(socket: Socket): Socket {
        socket.sendBufferSize = sendBufferSize
        return socket
    }

    companion object {
        const val DEFAULT_BUFFER_SIZE = 2048
    }
}
val clientBuilder = OkHttpClient.Builder()
    .socketFactory(ProgressFriendlySocketFactory())

正如其他人所提到的,记录请求正文可能会影响这一点,并导致数据被多次读取。要么不记录正文,要么为CountingRequestBody关闭它。为此,我编写了自己的HttpLoggingInterceptor,它解决了这个问题和其他问题(比如记录MultipartBody)。但这超出了这个问题的范围。

if(requestBody is CountingRequestBody) {
  // don't log the body in production
}

其他问题与MockWebServer有关。我有一个使用MockWebServer和json文件的口味,这样我的应用程序就可以在没有网络的情况下运行,这样我就可以在没有负担的情况下进行测试。为了让这段代码工作,调度程序需要读取正文数据。我创建这个调度程序就是为了做到这一点。然后它将分派转发给另一个分派器,例如默认的QueueDispatcher。

class BodyReadingDispatcher(val child: Dispatcher): Dispatcher() {

    override fun dispatch(request: RecordedRequest?): MockResponse {
        val body = request?.body
        if(body != null) {
            val sink = ByteArray(1024)
            while(body.read(sink) >= 0) {
                Thread.sleep(50) // change this time to work for you
            }
        }
        val response = child.dispatch(request)
        return response
    }
}

您可以在MockWebServer中将其用作:

var server = MockWebServer()
server.setDispatcher(BodyReadingDispatcher(QueueDispatcher()))
 类似资料:
  • 我试图实现一个进度条来指示一个多部分文件上传的进度。 有人能举个例子说明我会怎么做吗?

  • 对于一个新项目,我想使用Jenkins CI的新管道功能。我们的Git存储库中有几个分支,应该以同样的方式进行测试。它还应该自动跟踪和处理新的分支。因此,我创建了一个多分支管道作业。但它的配置有两个问题: 1) 为了被Jenkins标记为有效,分行需要一个“Jenkinsfile”。如果这不存在,詹金斯将忽略该分支。有没有办法标记与模式匹配的所有分支,而不需要在其中包含此文件? 2) 每个分支都应

  • 我们正在构建基于java的GUI以将非常大的文件上传到S3桶。我们从API获取预先签名的URL,然后点击此URL上传我们的文件。 现在,由于文件大小非常大(10 GB),我们希望显示用户数据传输统计数据(例如,传输了多少数据、传输速率等)。 重要提示-我们没有AWS ID-KEY来访问bucket,所以我们不能使用AWS SDK。 任何一个指针都会很有帮助,我们将不胜感激。

  • 本章介绍如何使用Zipkin或Jaeger收集启用了Istio的应用程序的调用链信息。 完成本章后,你可以理解有关应用程序的所有假设以及如何使其参与跟踪,无论您使用何种语言/框架/平台构建应用程序。 BookInfo示例用来作为此任务的示例应用程序。 环境准备 参照安装指南的说明安装Istio。 如果您在安装过程中未启动Zipkin或Jaeger插件,则可以运行以下命令启动: 启动Zipkin:

  • 当我将单体应用拆成多个微服务之后,如何监控服务之间的依赖关系和调用链,以判断应用在哪个服务环节出了问题,哪些地方可以优化?这就需要用到分布式追踪(Distributed Tracing)。 CNCF 提出了分布式追踪的标准 OpenTracing,它提供用厂商中立的 API,并提供 Go、Java、JavaScript、Python、Ruby、PHP、Objective-C、C++ 和 C# 这九

  • 问题内容: 当我在CI服务器上调试单元测试之一(实际上是Maven构建)时,发生了有趣的事情。我连接到Java进程以跟踪构建进程的网络活动。这就是我所看到的: 因此,我们有一些网络活动(这实际上是我要搜索的),并且有很多信号。 构建正确完成(仅一项失败的测试)。情况可以确定并一遍又一遍地再现。这是什么意思? 问题答案: 因为这是Java,所以这意味着您的JVM正在使用SIGSEGV。常见用途包括