如何使用Scala的sttp客户端下载文件。

huangapple go评论56阅读模式
英文:

How to use scala sttp client to download file

问题

我有任务要检查AWS定价,它是一个公共的JSON文件,不需要认证。

我使用了Tapir Sttp库。

val request: Request[Either[String, String], Any] = basicRequest
    .header(HeaderNames.Accept, MediaType.ApplicationOctetStream.toString())
    .get(uri"https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")

val response = request.send(sttpBackend)
val res = Await.result(response, 30.seconds)

以上是我的简单GET请求。
但是我遇到了错误:
导致: java.io.UnsupportedEncodingException: 不支持的编码

我可以从浏览器下载文件,从Postman获取JSON。
有人知道如何使用sttp客户端下载文件吗?
谢谢

尝试从Amazon定价终端点下载JSON文件。

英文:

I have task to check AWS pricing, it's a public json file. there is no auth needed.

I use Tapir Sttp library

    val request: Request[Either[String, String], Any] = basicRequest
        .header(HeaderNames.Accept, MediaType.ApplicationOctetStream.toString())
        .get(uri"https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")


    val response = request.send(sttpBackend)
    val res = Await.result(response, 30.seconds)

above is my simple get call.
But I got error
Caused by: java.io.UnsupportedEncodingException: Unsupported encoding

I can download the file from browers, get the json from Postman.
Anybody know how to download file by sttp client
Thanks

try to download json file from amazon pricing endpoint

答案1

得分: 4

代码部分不需要翻译。以下是翻译好的部分:

看起来 AWS 终端点返回了一个带有空的 Content-Encoding 头的响应:

> GET /offers/v1.0/aws/index.json HTTP/1.1
> Host: pricing.us-east-1.amazonaws.com
> User-Agent: curl/7.88.1
> Accept: */*
<
< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
(...)
< x-amz-server-side-encryption: AES256
< Content-Encoding:
< x-amz-version-id: RPn8P8KKFZDsbbPZSHSPEAxgOPJ4okmH
(...)

我们的代码试图将一个支持的解码算法与头的值进行匹配,如果存在的话。这会失败,因为空字符串没有解码算法。

我已经创建了一个问题来在 sttp 中修复这个问题。修复方法是忽略头的空值。

还有一个解决方法:你可以提供一个自定义的编码处理程序来“处理”空值。例如,使用 HttpURLConnectionBackend

object Test extends App {
  val request: Request[Either[String, String], Any] = basicRequest
    .header(HeaderNames.Accept, MediaType.ApplicationOctetStream.toString())
    .get(uri"https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")

  val backend = HttpURLConnectionBackend(customEncodingHandler = { case (is, "") => is })

  println(request.send(backend))
}
英文:

It seems the aws endpoint returns a response with an empty Content-Encoding header:

&gt; GET /offers/v1.0/aws/index.json HTTP/1.1
&gt; Host: pricing.us-east-1.amazonaws.com
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt;
&lt; HTTP/1.1 200 OK
&lt; Content-Type: application/octet-stream
(...)
&lt; x-amz-server-side-encryption: AES256
&lt; Content-Encoding:
&lt; x-amz-version-id: RPn8P8KKFZDsbbPZSHSPEAxgOPJ4okmH
(...)

Our code tries to match one of the supported decoding algorithms to the value of the header, if it is present. This fails, as there is no algorithm for an empty string.

I've created an issue to fix that in sttp. The fix is to ignore empty values of the header.

There is also a work-around: you can provide a custom encoding handler to "handle" the empty value. For example, using the HttpURLConnectionBackend:

object Test extends App {
  val request: Request[Either[String, String], Any] = basicRequest
    .header(HeaderNames.Accept, MediaType.ApplicationOctetStream.toString())
    .get(uri&quot;https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json&quot;)

  val backend = HttpURLConnectionBackend(customEncodingHandler = { case (is, &quot;&quot;) =&gt; is })

  println(request.send(backend))
}

答案2

得分: 0

我实际上在上述提到的URL上尝试了curlContent-Encoding: 部分为空。

并且 sttp.client3.HttpClientSyncBackend.standardEncoding 因为空的 Content-Encoding 而失败:https://github.com/softwaremill/sttp/blob/v3.8.15/core/src/main/scalajvm/sttp/client3/HttpClientSyncBackend.scala#LL64C16-L64C16

因此,您需要绕过 standardEncoding 并创建一个具有自定义编码的后端,以处理空的 Content-Encoding 标头,方法是提供一个简单的自定义编码处理程序 (customEncodingHandler: [(InputStream, String) => InputStream] = (is, _) => is)

val backend = HttpClientSyncBackend(customEncodingHandler = (is, _) => is)

// 创建您的请求
val request = ???

val response = request.send(backend)
val res = Await.result(response, 30.seconds)
英文:

I actually tried curl on the above mentioned URL, the Content-Encoding: is coming as empty.

curl -X GET https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json --output ~/Desktop/pricing-index.json -v
Note: Unnecessary use of -X or --request, GET is already inferred.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 54.192.150.79:443...
* Connected to pricing.us-east-1.amazonaws.com (54.192.150.79) port 443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1

## &lt;-- redacted --&gt;

&gt; GET /offers/v1.0/aws/index.json HTTP/1.1
&gt; Host: pricing.us-east-1.amazonaws.com
&gt; User-Agent: curl/7.87.0
&gt; Accept: */*
&gt;
* Mark bundle as not supporting multiuse
&lt; HTTP/1.1 200 OK
&lt; Content-Type: application/octet-stream
&lt; Content-Length: 69431
&lt; Connection: keep-alive
&lt; Date: Tue, 13 Jun 2023 06:29:27 GMT
&lt; Last-Modified: Tue, 13 Jun 2023 01:42:43 GMT
&lt; x-amz-server-side-encryption: AES256
&lt; Content-Encoding:

## &lt;-- redacted --&gt;

&lt;
{ [1455 bytes data]
100 69431  100 69431    0     0  41107      0  0:00:01  0:00:01 --:--:-- 41229
* Connection #0 to host pricing.us-east-1.amazonaws.com left intact

And the sttp.client3.HttpClientSyncBackend.standardEncoding is failing due to empty Content-Encoding : https://github.com/softwaremill/sttp/blob/v3.8.15/core/src/main/scalajvm/sttp/client3/HttpClientSyncBackend.scala#LL64C16-L64C16

So, you need to bypass the standardEncoding and create a backend with custom encoding to handle the empty Content-Encoding header by providing just a pass-through customEncodingHandler ( customEncodingHandler: [(InputStream, String) =&gt; InputStream] = (is, _) =&gt; is )

I

val backend = HttpClientSyncBackend(customEncodingHandler = (is, _) =&gt; is)

// create your request
val request = ???

val response = request.send(backend)
val res = Await.result(response, 30.seconds)

huangapple
  • 本文由 发表于 2023年6月13日 05:55:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76460554.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定