Unit 13 - Restful 傳回 PNG 圖檔

3 minute read

JSF 教學

Unit 13

簡介

期望提供圖片資源供其它的客戶端使用。

例如,使用 Restful Service 提供圖片。Service End Point: /app_context/img/{name}。URL 範例: http://localhost:8080/unit13/rest/img/test.png

另一種方式,可直接提供圖片的 URL,例如: http://localhost:8080/unit13/resources/images/test.png

以下介紹利用 Restful Service 提供圖片資源,過程中讓我們可以修改圖片的內容,例如: 加上檔案名稱。

重要實作、類別及其方法

取得 Restful Application 的 Context

需要取得 Servlet Context 後,我們才能取得應用程式內的資源的實際所在路徑(real path of the resource)或者取得資源成為串流 (resource as stream)。

在 EJB 中注入 ServletContext 物件,取得 Restful Application 的 Context:

1
2
3
4
5
6
7
8
9
@Stateless
@Path("/img")
public class ImageResource {
    
    //#1
    @Context 
    private ServletContext servletContext;

}

ServletContext 提供 getRealPath()getResourceAsStream() 兩個重要方法。前者傳回資源的實際所在路徑,後者傳回 InputStream

讀取圖檔後直接輸出

只單純取得圖形的內容後即輸出給客戶端。

第一種情況:圖若片是放在一般目錄下。此時,提供 Web Service 的 end-point 給客戶端提出請求。應用程式將圖形放在 Response 的 Payload 中,回傳給客戶端。例如說明,給客戶端的 Restful end-point: http://localhost:8080/unit13/rest/img/direct/test.png

在 Server 端的請求的的處理程序為:

  1. 取得圖形檔案的串流
  2. 將串流放到 Response object 中回傳給 Client。

Demo Codes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Stateless
@Path("/img")
public class ImageResource {
    
    //#1
    @Context 
    private ServletContext servletContext;
    
    @GET
    @Path("ping")
    public String getServerTime() {
        System.out.println("RESTful Service 'ImageResource' is running ==> ping");
        return "received ping on "+ new Date().toString();
    }
    
    @GET
    // Path: /img/direct/{name}
    // #2
    @Path("direct/{name}")
    @Produces("image/png")
    public Response findImageDirectStream(@PathParam("name") String filename){
        String fullName = "/resources/images/"+filename;
        //#3
        InputStream imageData = servletContext.getResourceAsStream(fullName);
        //#4
        return Response.ok(imageData).build();
    }

說明:

  1. 注入 ServletContext 物件。Restful Application 不是使用 FacesContext,JSF Application 才使用它取得應用程式的 Context。使用 ServletContext.getRealPath() 取得給定資源在 Application Server 上的實際路徑。
  2. 設定 GET request handler 要回傳的資料型態為 image/png
  3. 使用 servletContext.getResourceAsStream() 取得資源的串流。
  4. 把資源串流放到 Response 的 payload 中。

還有其它的情況,如: 圖片放在資料庫中,在這裡不做說明。

讀取圖檔到 Image 物件

如果要對圖形做處理後再輸出,例入加上文字在圖片中,則必須先把圖片讀到 Image 物件中。

使用 ImageIO 物件讀取圖檔的方法:

  • read(): 讀取圖檔放到 Image 物件
1
2
public static BufferedImage read(File input)
                          throws IOException

Ref: read() - Java SE 7

Example:

1
2
3
4
5
6
7
8
9
10
11
 BufferedImage image = null;
        
 String fullName = "/resources/images/"+filename;
 String webAppRealPathName = servletContext.getRealPath(fullName);
 System.out.println(webAppRealPathName);
 
 try {
     image = ImageIO.read(new File(webAppRealPathName));
 } catch (IOException ex) {
     Logger.getLogger(ImageResource.class.getName()).log(Level.SEVERE, null, ex);
 }

Image 物件輸出為串流

  • write(): 將 Image 物件寫到輸出串流 OutputStream
    1
    2
    3
    4
    
    public static boolean write(RenderedImage im,
              String formatName,
              OutputStream output) 
              throws IOException
    

    Ref: ImageIO (Java Platform SE 7 )

Example

1
2
3
4
5
6
7
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", baos);
        } catch (IOException ex) {
            Logger.getLogger(ImageResource.class.getName()).log(Level.SEVERE, null, ex);
        }
    byte[] imageData = baos.toByteArray();

將圖檔讀取到 Java 程式內更多參考資料:

完整的程式碼

Restful Service 程式樣版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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();
}

Source: How to return a PNG image from Jersey REST service method to the browser

完整程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Stateless
@Path("/img")
public class ImageResource {
    
     @Context 
     private ServletContext servletContext;
    
    @GET
    @Path("ping")
    public String getServerTime() {
        System.out.println("RESTful Service 'ImageResource' is running ==> ping");
        return "received ping on "+ new Date().toString();
    }
    
    @GET
    // Path: /img/{name}
    @Path("{name}")
    @Produces("image/png")
    public Response findImage(@PathParam("name") String filename){
        BufferedImage image = null;
        
        String fullName = "/resources/images/"+filename;
        String webAppRealPathName = servletContext.getRealPath(fullName);
        System.out.println(webAppRealPathName);
        
        try {
            image = ImageIO.read(new File(webAppRealPathName));
        } catch (IOException ex) {
            Logger.getLogger(ImageResource.class.getName()).log(Level.SEVERE, null, ex);
        }
    // 修改圖片內容
        Graphics2D g2d = image.createGraphics();
        String addedText = filename;
        g2d.setColor(Color.BLACK);
        g2d.setFont(new Font("Default", Font.BOLD, 25));
        g2d.drawString(addedText, 50, 50);//置入文字 (x,y)

    // Image 存成 ByteArray
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", baos);
        } catch (IOException ex) {
            Logger.getLogger(ImageResource.class.getName()).log(Level.SEVERE, null, ex);
        }
    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();
    }
}

實作成果

Case 1: 用 Restful 存取 png 圖檔

Case 2: 用 Restful 存取 png 圖檔, 過程中修改圖檔內容

Case 3: 使用 uri 直接取得圖片,沒有使用 Restful Service

Updated: