2012-02-09 11 views
38

Jersey RESTリソースを使用して実行しているWebサーバーがあり、ブラウザのimgタグのイメージ/ png参照を取得する方法がわかります。フォームを送信したり、Ajaxレスポンスを受け取ったりすることができます。グラフィックスを追加するための画像処理コードが機能していて、何とか戻す必要があります。Jersey RESTサービスメソッドからPNGイメージをブラウザに返す方法

コード:

@POST 
@Path("{fullsize}") 
@Consumes(MediaType.MULTIPART_FORM_DATA) 
@Produces("image/png") 
// Would need to replace void 
public void getFullImage(@FormDataParam("photo") InputStream imageIS, 
         @FormDataParam("submit") String extra) { 

     BufferedImage image = ImageIO.read(imageIS); 

     // .... image processing 
     //.... image processing 

     return ImageIO. .. ? 

} 

乾杯

+0

あなたは何を達成しようとしていますか?イメージの場所にURIを送ることでこれを達成できませんか? – Perception

+0

注文する前に写真上で選択したグラフィックスをプレビューするようにします。私はAJAXの投稿でこれを行うことはできません今、あなたが処理された画像を指して言ったようにWebページを要求する必要があります参照してください。 – gorn

答えて

79

Iは、RESTサービスに画像データを返すためにその良いアイデアを確信していません。これは、アプリケーションサーバーのメモリとIOの帯域幅を結び付けます。この種の転送に最適化された適切なWebサーバーにそのタスクを委任するほうがずっと優れています。これを達成するには、イメージリソースにリダイレクトを送信します(イメージのURIを使用したHTTP 302応答として)。これはもちろん、あなたの画像がWebコンテンツとして整理されていることを前提としています。

は、あなたが本当にあなたを決めるWebサービスから画像データを転送する必要がある場合は、以下の(擬似)コードでそうすることができる、と述べた:

@Path("/whatever") 
@Produces("image/png") 
public Response getFullImage(...) { 

    BufferedImage image = ...; 

    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ImageIO.write(image, "png", baos); 
    byte[] imageData = baos.toByteArray(); 

    // 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(); 
} 

は、などなど、例外処理に

を追加します
+0

ありがとう!それがそれを行う一つの方法です。 – gorn

+0

cURLを使用してこのRESTful Java Webサービスからイメージを取得し、HTMLイメージタグでそれらをポイントしたPHPサーバーアプリケーションで終了しました。 – gorn

+0

@gornあなたの答えにあなたの解答を編集として書くべきである – kommradHomer

11

私は、次の機能とそのための一般的な方法を構築しました:

  • をファイルはStatus.NOT_MODIFIEDが呼び出し元に送信され、ローカルで変更されていない場合は、「変更されません」を返します。ロケールの切り替えは、スレッドセーフであると思わないこと

    import org.apache.commons.lang3.time.DateUtils; 
    import org.slf4j.Logger; 
    import org.slf4j.LoggerFactory; 
    
    private static final Logger logger = LoggerFactory.getLogger(Utils.class); 
    
    @GET 
    @Path("16x16") 
    @Produces("image/png") 
    public Response get16x16PNG(@HeaderParam("If-Modified-Since") String modified) { 
        File repositoryFile = new File("c:/temp/myfile.png"); 
        return returnFile(repositoryFile, modified); 
    } 
    
    /** 
    * 
    * Sends the file if modified and "not modified" if not modified 
    * future work may put each file with a unique id in a separate folder in tomcat 
    * * use that static URL for each file 
    * * if file is modified, URL of file changes 
    * * -> client always fetches correct file 
    * 
    *  method header for calling method public Response getXY(@HeaderParam("If-Modified-Since") String modified) { 
    * 
    * @param file to send 
    * @param modified - HeaderField "If-Modified-Since" - may be "null" 
    * @return Response to be sent to the client 
    */ 
    public static Response returnFile(File file, String modified) { 
        if (!file.exists()) { 
         return Response.status(Status.NOT_FOUND).build(); 
        } 
    
        // do we really need to send the file or can send "not modified"? 
        if (modified != null) { 
         Date modifiedDate = null; 
    
         // we have to switch the locale to ENGLISH as parseDate parses in the default locale 
         Locale old = Locale.getDefault(); 
         Locale.setDefault(Locale.ENGLISH); 
         try { 
          modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS); 
         } catch (ParseException e) { 
          logger.error(e.getMessage(), e); 
         } 
         Locale.setDefault(old); 
    
         if (modifiedDate != null) { 
          // modifiedDate does not carry milliseconds, but fileDate does 
          // therefore we have to do a range-based comparison 
          // 1000 milliseconds = 1 second 
          if (file.lastModified()-modifiedDate.getTime() < DateUtils.MILLIS_PER_SECOND) { 
           return Response.status(Status.NOT_MODIFIED).build(); 
          } 
         } 
        }   
        // we really need to send the file 
    
        try { 
         Date fileDate = new Date(file.lastModified()); 
         return Response.ok(new FileInputStream(file)).lastModified(fileDate).build(); 
        } catch (FileNotFoundException e) { 
         return Response.status(Status.NOT_FOUND).build(); 
        } 
    } 
    
    /*** copied from org.apache.http.impl.cookie.DateUtils, Apache 2.0 License ***/ 
    
    /** 
    * Date format pattern used to parse HTTP date headers in RFC 1123 format. 
    */ 
    public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; 
    
    /** 
    * Date format pattern used to parse HTTP date headers in RFC 1036 format. 
    */ 
    public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz"; 
    
    /** 
    * Date format pattern used to parse HTTP date headers in ANSI C 
    * <code>asctime()</code> format. 
    */ 
    public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; 
    
    public static final String[] DEFAULT_PATTERNS = new String[] { 
        PATTERN_RFC1036, 
        PATTERN_RFC1123, 
        PATTERN_ASCTIME 
    }; 
    

    注:ここでは、コードをファイル自体

をファイルストリームオブジェクトを使用して代わりに読んApache Commons Lang

  • 使用しています。私はロケールをグローバルに切り替えるほうがよいと思います。私は副作用については確信していませんが...

  • +3

    JerseyのRequest.evaluatePreconditions(...)を使用すると、日付の解析と検査を処理し、サポートしている場合はetagsを使用して、最後に変更されたロジックをたくさん削除できます。 – bramp

    6

    の答えは@Perceptionから、バイト配列を使って作業するときは非常にメモリが必要ですが、出力ストリームに単純に書き戻すこともできます

    @Path("/picture") 
    public class ProfilePicture { 
        @GET 
        @Path("/thumbnail") 
        @Produces("image/png") 
        public StreamingOutput getThumbNail() { 
        return new StreamingOutput() { 
         @Override 
         public void write(OutputStream os) throws IOException, WebApplicationException { 
         //... read your stream and write into os 
         } 
        }; 
        } 
    } 
    
    3

    あなたがイメージリソースメソッドの数を持っている場合、それは出力にBufferedImageをMessageBodyWriterを作成する価値がある:

    @Produces({ "image/png", "image/jpg" }) 
    @Provider 
    public class BufferedImageBodyWriter implements MessageBodyWriter<BufferedImage> { 
        @Override 
        public boolean isWriteable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) { 
        return type == BufferedImage.class; 
        } 
    
        @Override 
        public long getSize(BufferedImage t, Class<?> type, Type type1, Annotation[] antns, MediaType mt) { 
        return -1; // not used in JAX-RS 2 
        } 
    
        @Override 
        public void writeTo(BufferedImage image, Class<?> type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap<String, Object> mm, OutputStream out) throws IOException, WebApplicationException { 
        ImageIO.write(image, mt.getSubtype(), out); 
        } 
    } 
    

    自動検出がジャージーのために有効になっている場合、このMessageBodyWriterが自動的に使用されます、そうでなければ、それは後退する必要がありますカスタムのアプリケーションサブクラスによって実装されます。詳細はJAX-RS Entity Providersを参照してください。

    これが設定されると、単にリソースメソッドからBufferedImageを返し、それが画像ファイルデータなどとすることが出力される。

    このアプローチの利点のカップル:

    • 中間のBufferedOutputStreamではなくOutputSteamに書き込む
    • これは、リソースメソッドで許可されているメディアタイプに応じて、pngとjpgの両方の出力をサポートします。
    関連する問題