服务器生成的映像文件已损坏/不正确

时间:2012-12-31 16:47:35

标签: java image jersey apache-httpclient-4.x content-encoding

我正在使用Jersey(版本1.9.1)为png图像实现RESTful Web服务。我在客户端使用Apache HttpClient(版本4x)。客户端的代码调用HttpGet来下载图像。成功下载后,它会将InputStream从HttpEntity保存到磁盘。现在问题是结果文件和服务器上的文件是不同的。客户端代码生成的输出图像文件不是可渲染的。

@GET
@Path("/public/profile/{userId}")
@Produces({ "image/png" })
public Response getImage(@PathParam(value = "userId") String userId) {
    Response res = null;
    // ImageManagement.gerProfilePicture(userId) returns me profile picture
    // of the provided userId in PathParam
    File imageFile = ImageManagement.getProfilePicture(userId);
    if (imageFile == null) {
        res = Response.status(Status.NOT_FOUND).build();
    } else {
        res = Response
                .ok(imageFile, "image/png")
                .header("Content-Disposition",
                        "attachment; filename=Img" + userId + ".png")
                .build();
    }
    return res;
}

我的客户端代码调用上面的资源方法

private File downloadProfilePicture(String userId) throws IOException{
    // URIHelper is a utility class, this give me uri for image resource
    URI imageUri = URIHelper.buildURIForProfile(userId);

    HttpGet httpGet = new HttpGet(imageUri);
    HttpResponse httpResponse = httpClient.execute(httpGet);
    int statusCode = httpResponse.getStatusLine().getStatusCode();

    File imageFile = null;
    if (statusCode == HttpURLConnection.HTTP_OK) {
        HttpEntity httpEntity = httpResponse.getEntity();
        Header[] headers = httpResponse.getHeaders("Content-Disposition");
        imageFile = new File(OUTPUT_DIR, headers[0].getElements()[0]
                .getParameterByName("filename").getValue());
        FileOutputStream foutStream = new FileOutputStream(imageFile);
        httpEntity.writeTo(foutStream);
        foutStream.close();
    }
    return imageFile;
}

现在问题是服务器上存在文件,下载的文件不同。

以下是服务器上存在的文件转储。

Dump of the file on server

下面是下载文件的转储。

Dump of the file on client

你可以看到,一些字节正在改变。 Jersey服务器api是否修改了文件流中的数据?出了什么问题?

更新

如果我从浏览器点击同一个网址,则会下载该文件,但下载的文件无法查看。所以问题似乎与服务器有关。

3 个答案:

答案 0 :(得分:1)

对服务器采取不同的方法。无论是documented in the Jersey manual还是像这样:

@GET
@Path("/public/profile/{userId}")
@Produces("image/png")
public Response getFullImage(...) {

    Path path = Paths.get("path/to/file");
    byte[] imageData = Files.readAllBytes(path);

    // uncomment line below to send non-streamed
    // return Response.ok(imageData).build();

    // uncomment line below to send streamed
    // return Response.ok(new ByteArrayInputStream(imageData)).build();
}

旁注:我认为在REST服务中返回图像数据并不是一个好主意。它占用了服务器的内存和I / O带宽。

答案 1 :(得分:1)

我会尝试返回输入流而不是File对象。我认为媒体类型可能会搞乱,或者默认文件处理正在弄乱输出。所以使用may:

Response.ok(new FileInputStream(imageFile),“image / png”)                 .header(“Content-Disposition”,“attachment; filename = Img”+ userId +“。png”)                 .build();

答案 2 :(得分:0)

我发现这是我的错。我正在修改过滤器代码中的响应数据(通过更改它的编码)。此过滤器用于设置内容长度标头并处理“eTag”。这个想法来自这里:http://www.infoq.com/articles/etags

@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    HttpServletRequest servletRequest = (HttpServletRequest) request;
    HttpServletResponse servletResponse = (HttpServletResponse) response;

    HttpResponseCatcher wrapper = new HttpResponseCatcher(
            (HttpServletResponse) response);

    chain.doFilter(request, wrapper);

    final byte[] responseBytes = wrapper.getByteArray();

    String digest = getMd5Digest(responseBytes);

    String etag = '"' + digest + '"';
    // always store the ETag in the header
    servletResponse.setHeader("ETag", etag);

    String previousEtag = servletRequest.getHeader("If-None-Match");
    // compare previous token with current one
    if (previousEtag != null && previousEtag.equals(etag)) {
        servletResponse.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        // use the same date we sent when we created the ETag the first time
        // through
        servletResponse.setHeader("Last-Modified",
                servletRequest.getHeader("If-Modified-Since"));
    } else {
        // first time through - set last modified time to now
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.MILLISECOND, 0);
        Date lastModified = cal.getTime();
        servletResponse.setDateHeader("Last-Modified",
                lastModified.getTime());

        servletResponse.setContentLength(responseBytes.length);
        ServletOutputStream sos = servletResponse.getOutputStream();
        sos.write(responseBytes);
        sos.flush();
        sos.close();
    }
}

我有一个扩展HttpServletResponseWrapper的HttpResponseCacher类。

public class HttpResponseCatcher extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer;

    public HttpResponseCatcher(HttpServletResponse res) {
        super(res);
        this.buffer = new ByteArrayOutputStream();
    }

    //There is some more code in the class, but that is not relevant to the problem...
    public byte[] getByteArray() {
        //The problem is here... this.buffer.toString().getBytes() changes to encoding of the data      
        return this.buffer.toString().getBytes();
    }
}

我将byte[] getByteArray()中的代码从return this.buffer.toString().getBytes();更改为return this.buffer.toByteArray();,这解决了问题。