【问题标题】:How do I get a byte[] (image) from a webservice using micronaunt HttpClient如何使用 micronaut HttpClient 从 Web 服务获取字节 [](图像)
【发布时间】:2019-08-28 18:54:17
【问题描述】:

我正在将一个 Grails 3.1 库移植到 Grails 4.0,以便使用一些内部 Web 服务。其中一项服务根据请求提供所请求员工的图像。我在实现(micronaut)HttpClient 代码来处理请求时遇到了困难——特别是要获得正确的byte[],即返回的图像。

命令行上的一个简单 curl 命令可以与服务一起使用:

curl -D headers.txt -H 'Authorization:Basic <encodedKeyHere>' https:<serviceUrl> >> image.jpg

而且图像是正确的。 header.txt 是:

HTTP/1.1 200 
content-type: image/jpeg;charset=UTF-8
date: Tue, 27 Aug 2019 20:05:43 GMT
x-ratelimit-limit: 100
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
x-ratelimit-remaining: 99
X-RateLimit-Reset: 38089
x-ratelimit-reset: 15719
Content-Length: 11918
Connection: keep-alive

旧库使用groovyx.net.http.HTTPBuilder 并且简单地这样做了:

http.request(Method.GET, ContentType.BINARY) {
            uri.path = photoUrlPath
            uri.query = queryString
            headers.'Authorization' = "Basic $encoded".toString()
            response.success = { resp, inputstream ->
                log.info "response status: ${resp.statusLine}"                
                return ['status':resp.status, 'body':inputstream.getBytes()]
            }
            response.failure = { resp ->
                return ['status':resp.status, 
                        'error':resp.statusLine.reasonPhrase, 
                         body:resp.getEntity().getContent().getText()]
            }
        }

所以从输入流返回字节。这行得通。

我已经使用 micronaut HttpClient 尝试了几件事,包括低级 API 和声明式 API。

声明式 API 的简单示例:


    @Get(value='${photo.ws.pathurl}', produces = MediaType.IMAGE_JPEG)
    HttpResponse<byte[]> getPhoto(@Header ('Authorization') String authValue, 
                                  @QueryValue("emplId") String emplId)

比在服务中:

    HttpResponse<byte[]> resp = photoClient.getPhoto(getBasicAuth(),emplId)
    def status = resp.status()            // code == 200 --> worked 
    def bodyStrOne = resp.getBody()       // nope: get Optional.empty 
    // Tried different getBody(class) -> Can't figure out where the byte[]s are   
    // For example can do:
    def buf = resp.getBody(io.netty.buffer.ByteBuf).value // Why need .value?
    def bytes = buf.readableBytes()       // Returns 11918 --> the expected value
    byte[] ans = new byte[buf.readableBytes()]
    buf.readBytes(ans)    // Throws exception: io.netty.util.IllegalReferenceCountException: refCnt: 0

这“有效”,但返回的字符串丢失了一些我无法反转的编码:

   // Client - use HttpResponse<String>
   @Get(value='${photo.ws.pathurl}', produces = MediaType.IMAGE_JPEG)
   HttpResponse<String> getPhoto(@Header ('Authorization') String authValue, 
                                  @QueryValue("emplId") String emplId)
   // Service 
   HttpResponse<String> respOne = photoClient.getPhoto(getBasicAuth(),emplId)
   def status = respOne.status()                  // code == 200 --> worked 
   def bodyStrOne = respOne.getBody(String.class) // <-- RETURNS DATA..just NOT an Image..or encoded or something
   String str = bodyStrOne.value                  // get the String data  
   // But these bytes aren't correct
   byte[] ans = str.getBytes()                    // NOT an image..close but not.
   // str.getBytes(StandardCharsets.UTF_8) or any other charset doesn't work 

我对 ByteBuf 类所做的一切尝试都会引发 io.netty.util.IllegalReferenceCountException: refCnt: 0 异常。

任何方向/帮助将不胜感激。

跑步:

    Grails   4.0 
    JDK      1.8.0_221 
    Groovy   2.4.7
    Windows  10
    IntellJ  2019.2  

【问题讨论】:

    标签: download binary httpclient micronaut micronaut-client


    【解决方案1】:

    一定是 Grails 的 bug。


    将此行添加到logback.groovy:

    logger("io.micronaut.http", TRACE)
    

    然后你应该看到正文不是空的,但最后它以错误Unable to convert response body to target type class [B 结束。查看痕迹:

    2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Status Code: 200 OK
    2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Content-Type: image/jpeg
    2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Content-Length: 11112
    2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Accept-Ranges: bytes
    2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Response Body
    2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ----
    2019-09-11 11:19:16.238 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ���� C   
    ...
    2019-09-11 11:19:16.241 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ----
    2019-09-11 11:19:16.243 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Unable to convert response body to target type class [B
    

    但是当您在独立的 Microunaut 应用程序中尝试相同的操作(将 &lt;logger name="io.micronaut.http" level="trace"/&gt; 添加到 logback.xml)时,结果会有所不同:

    09:02:48.583 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Status Code: 200 OK
    09:02:48.583 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Content-Type: image/jpeg
    09:02:48.589 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - content-length: 23195
    09:02:48.590 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Response Body
    09:02:48.590 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ----
    09:02:48.612 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ���� C���
    ...
    09:02:48.620 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ----
    

    Micronaut 跟踪没有错误。


    这是一个从https://picsum.photos网站下载随机图像的声明式HTTP客户端示例:

    import io.micronaut.http.HttpResponse
    import io.micronaut.http.MediaType
    import io.micronaut.http.annotation.Get
    import io.micronaut.http.client.annotation.Client
    
    @Client('https://picsum.photos')
    interface LoremPicsumClient {
        @Get(value = '{width}/{height}', produces = MediaType.IMAGE_JPEG)
        HttpResponse<byte[]> getImage(Integer width, Integer height)
    }
    

    Spock 对其进行单元测试:

    import io.micronaut.http.HttpStatus
    import io.micronaut.test.annotation.MicronautTest
    import spock.lang.Specification
    
    import javax.inject.Inject
    import java.nio.file.Files
    import java.nio.file.Paths
    
    @MicronautTest
    class LoremPicsumClientSpec extends Specification {
        @Inject
        LoremPicsumClient client
    
        void 'image is downloaded'() {
            given:
            def output = Paths.get('test')
    
            when:
            def response = client.getImage(300, 200)
    
            then:
            response.status == HttpStatus.OK
            response.getBody().isPresent()
    
            when:
            Files.write(output, response.getBody().get())
    
            then:
            Files.probeContentType(output) == 'image/jpeg'
        }
    }
    

    在 Micronaut 中,测试通过并且图像被保存到 test 文件中。但是在 Grails 中,测试失败了,因为 HttpClient 无法将响应字节转换为 byte 数组,或者更好地转换为 String 之外的任何其他内容。

    【讨论】:

    • 好吧,我试过了,还是不行。与此同时,我使用 'io.github.http-builder-ng:http-builder-ng-core:1.0.4' 库完成了所有工作:`try { ans = HttpBuilder.configure { ignoreSslIssues execution request.headers ['授权'] = getBasicAuth() request.uri = theUrl request.uri.query = [huid: huid] }.get()`
    • 我通过添加 logback-test.xml 文件(我正在使用 Grails)并使用 HttpResponse 客户端打开跟踪,我得到 java.util.NoSuchElementException: No value present on即使状态为“Ok”,response.getBody().get() 跟踪表明它已打开(ch.qos.logback.classic.joran.action.LoggerAction - 记录器的设置级别 [io.micronaut.http.client ] 到 TRACE),但我没有从中获得任何可以在任何地方看到的其他信息。
    • 您的示例和我的示例之间的不同之处在于我的照片服务使用基本身份验证 - 这会导致问题吗!?
    • 对不起,我第一次在 Micronaut 中尝试过(因为我只使用 Micronaut)并且没有问题。现在我在 Grails 4 中尝试了同样的方法,我的小例子也和你的一样失败了。从我的角度来看,它一定是一个 Grails 错误。我更新了答案并在 Grails 中添加了来自跟踪的错误消息。
    【解决方案2】:

    我们目前正在使用这个实现:

    @Client(value = "\${image-endpoint}")
    interface ImageClient {
    
      @Get("/img")
      fun getImageForAddress(
        @QueryValue("a") a: String
      ): CompletableFuture<ByteArray>
    }
    

    对我们来说很好。

    当我使用 HttpResponse 时,我也收到了一个错误,无法让它工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-28
      • 2015-11-17
      相关资源
      最近更新 更多