Cách đơn giản nhất để phân phát dữ liệu tĩnh từ bên ngoài máy chủ ứng dụng trong ứng dụng web Java


131

Tôi có một ứng dụng web Java đang chạy trên Tomcat. Tôi muốn tải hình ảnh tĩnh sẽ được hiển thị cả trên Giao diện người dùng web và tệp PDF do ứng dụng tạo. Ngoài ra hình ảnh mới sẽ được thêm và lưu bằng cách tải lên thông qua giao diện người dùng web.

Không phải là vấn đề để làm điều này bằng cách lưu trữ dữ liệu tĩnh trong bộ chứa web nhưng việc lưu trữ và tải chúng từ bên ngoài bộ chứa web đang khiến tôi đau đầu.

Tôi không muốn sử dụng một máy chủ web riêng như Apache để phục vụ dữ liệu tĩnh tại thời điểm này. Tôi cũng không thích ý tưởng lưu trữ hình ảnh nhị phân trong cơ sở dữ liệu.

Tôi đã thấy một số đề xuất như có thư mục hình ảnh là một liên kết tượng trưng chỉ đến một thư mục bên ngoài bộ chứa web, nhưng cách tiếp cận này có hoạt động trên cả môi trường Windows và * nix không?

Một số đề nghị viết một bộ lọc hoặc một servlet để xử lý việc phục vụ hình ảnh nhưng những đề xuất đó rất mơ hồ và cao cấp mà không có con trỏ đến thông tin chi tiết hơn về cách thực hiện việc này.

Câu trả lời:


161

Tôi đã thấy một số đề xuất như có thư mục hình ảnh là một liên kết tượng trưng chỉ đến một thư mục bên ngoài bộ chứa web, nhưng cách tiếp cận này có hoạt động trên cả môi trường Windows và * nix không?

Nếu bạn tuân thủ các quy tắc đường dẫn hệ thống tập tin * nix (tức là bạn sử dụng các dấu gạch chéo chuyển tiếp như trong /path/to/files), thì nó cũng sẽ hoạt động trên Windows mà không cần phải loay hoay với các File.separatorchuỗi nối xấu xí . Tuy nhiên, nó sẽ chỉ được quét trên cùng một đĩa làm việc từ nơi lệnh này được gọi. Vì vậy, nếu Tomcat được cài đặt ví dụ trên C:thì /path/to/filesthực tế sẽ trỏ đến C:\path\to\files.

Nếu tất cả các tệp nằm bên ngoài ứng dụng web và bạn muốn có Tomcat DefaultServletđể xử lý chúng, thì tất cả những gì bạn cần làm trong Tomcat là thêm phần tử Ngữ cảnh sau /conf/server.xmlvào <Host>thẻ bên trong :

<Context docBase="/path/to/files" path="/files" />

Bằng cách này, họ sẽ có thể truy cập thông qua http://example.com/files/.... Ví dụ cấu hình GlassFish / Payara có thể được tìm thấy ở đây và ví dụ cấu hình WildFly có thể được tìm thấy ở đây .

Nếu bạn muốn tự mình kiểm soát việc đọc / ghi tệp, thì bạn cần phải tạo một cái Servletcho cái mà về cơ bản chỉ cần lấy một InputStreamtệp trong hương vị của ví dụ FileInputStreamvà ghi nó vàoOutputStream củaHttpServletResponse .

Về phản hồi, bạn nên đặt Content-Typetiêu đề để khách hàng biết ứng dụng nào sẽ liên kết với tệp được cung cấp. Và, bạn nên đặt Content-Lengthtiêu đề để khách hàng có thể tính toán tiến trình tải xuống, nếu không nó sẽ không xác định. Và, bạn nên đặt Content-Dispositiontiêu đề thành attachmentnếu bạn muốn hộp thoại Lưu dưới dạng , nếu không máy khách sẽ cố hiển thị nội tuyến. Cuối cùng chỉ cần ghi nội dung tệp vào luồng đầu ra phản hồi.

Đây là một ví dụ cơ bản của một servlet như vậy:

@WebServlet("/files/*")
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String filename = URLDecoder.decode(request.getPathInfo().substring(1), "UTF-8");
        File file = new File("/path/to/files", filename);
        response.setHeader("Content-Type", getServletContext().getMimeType(filename));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

Khi được ánh xạ trên một url-patternví dụ /files/*, sau đó bạn có thể gọi nó bằng http://example.com/files/image.png. Bằng cách này, bạn có thể kiểm soát các yêu cầu nhiều hơn so với yêu cầu DefaultServlet, chẳng hạn như cung cấp một hình ảnh mặc định (tức là if (!file.exists()) file = new File("/path/to/files", "404.gif")hoặc hơn). Ngoài ra, việc sử dụng request.getPathInfo()được ưu tiên ở trên request.getParameter()vì nó thân thiện với SEO hơn và nếu không IE sẽ không chọn đúng tên tệp trong Save As .

Bạn có thể sử dụng lại logic tương tự để phục vụ các tệp từ cơ sở dữ liệu. Đơn giản chỉ cần thay thế new FileInputStream()bằng ResultSet#getInputStream().

Hi vọng điêu nay co ich.

Xem thêm:


1
@SalutonMondo: cách ít nỗ lực nhất.
BalusC

@BalusC, tôi đã thử điều này: <Context docBase="/path/to/images" path="/images" />trong Windows, nhưng nhận được đường dẫn liên quan đến thư mục ứng dụng web:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV

Trên Windows phải là:<Context docBase="C:\tmp\" path="/images" />
ACV

Và nhận được HTTP Status 404 - /images/khi thực hiện : http://localhost:8080/images/, NHƯNG một tệp cụ thể từ bên dưới tmphoạt động: http://localhost:8080/images/Tulips.jpgvẫn ổn
ACV

Bằng cách này nếu tôi đặt một hình ảnh trong thư mục, liên kết đến hình ảnh sẽ trả lời 404 trừ khi bạn khởi động lại máy chủ, có lý do nào cho việc này không?
Mateus Viccari

9

Bạn có thể làm điều đó bằng cách đặt hình ảnh của mình trên một đường dẫn cố định (ví dụ: / var / hình ảnh hoặc c: \ hình ảnh), thêm một cài đặt trong cài đặt ứng dụng của bạn (được thể hiện trong ví dụ của tôi bởi Cài đặt. Class) và tải chúng như thế, trong một HttpServletcủa bạn:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
FileInputStream fis = new FileInputStream(filename);

int b = 0;
while ((b = fis.read()) != -1) {
        response.getOutputStream().write(b);
}

Hoặc nếu bạn muốn thao tác với hình ảnh:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
File imageFile = new File(filename);
BufferedImage image = ImageIO.read(imageFile);
ImageIO.write(image, "image/png", response.getOutputStream());

sau đó mã html sẽ là <img src="imageServlet?imageName=myimage.png" />

Tất nhiên bạn nên nghĩ đến việc phục vụ các loại nội dung khác nhau - "hình ảnh / jpeg", ví dụ dựa trên phần mở rộng tệp. Ngoài ra, bạn nên cung cấp một số bộ nhớ đệm.

Ngoài ra, bạn có thể sử dụng servlet này để định cỡ lại chất lượng hình ảnh của mình, bằng cách cung cấp các tham số chiều rộng và chiều cao làm đối số và sử dụng image.getScaledInstance(w, h, Image.SCALE_SMOOTH), xem xét hiệu suất, tất nhiên.


2
Bạn thực sự không cần API Java 2D cho việc này, nó chỉ cần thêm chi phí không cần thiết. Chỉ cần đọc InputStream và viết vào OutputStream.
BalusC

1
Yup, tôi bắt đầu phản hồi với ý tưởng thay đổi kích thước và các thao tác khác, nhưng cuối cùng đã đơn giản hóa nó.
Bozho

7

Thêm vào server.xml:

 <Context docBase="c:/dirtoshare" path="/dir" />

Cho phép tham số danh sách tệp dir trong web.xml:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>

Với sự thay đổi trong tệp web.xml tôi có thể lấy danh sách các tệp để gửi nó đến hộp chọn không?
Tomasz Waszchot

1
thay đổi này sẽ có trong tệp web của tomcat, không phải ứng dụng của bạn
vsingh

Cảm ơn vì điều đó! Có cho phép tham số danh sách tệp dir trong web.xml cần thiết không?
dariru

6

Yêu cầu: Truy cập Tài nguyên tĩnh (hình ảnh / video, v.v.,) từ bên ngoài thư mục WEBROOT hoặc từ đĩa cục bộ

Bước 1:
Tạo thư mục trong ứng dụng web của máy chủ tomcat. Hãy để chúng tôi nói tên thư mục là myproj

Bước 2:
Trong myproj, tạo thư mục WEB-INF trong phần này, tạo một tệp web.xml đơn giản

mã theo web.xml

<web-app>
</web-app>

Cấu trúc thư mục cho hai bước trên

c:\programfile\apachesoftwarefoundation\tomcat\...\webapps
                                                            |
                                                            |---myproj
                                                            |   |
                                                            |   |---WEB-INF
                                                                |   |
                                                                    |---web.xml

Bước 3:
Bây giờ tạo tệp xml có tên myproj.xml theo vị trí sau

c:\programfile\apachesoftwarefoundation\tomcat\conf\catalina\localhost

MÃ trong myproj.xml:

<Context path="/myproj/images" docBase="e:/myproj/" crossContext="false" debug="0" reloadable="true" privileged="true" /> 

Bước 4:
4 A) Bây giờ tạo một thư mục có tên myproj trong ổ E của đĩa cứng của bạn và tạo mới

thư mục với hình ảnh tên và đặt một số hình ảnh trong thư mục hình ảnh (e:myproj\images\)

Chúng ta hãy giả sử myfoto.jpg được đặt dưới e:\myproj\images\myfoto.jpg

4 B) Bây giờ tạo một thư mục có tên WEB-INF e:\myproj\WEB-INFvà tạo một tệp web.xml trong thư mục WEB-INF

Mã trong web.xml

<web-app>
</web-app>

Bước 5:
Bây giờ tạo tài liệu .html với tên index.html và đặt bên dưới e: \ myproj

CODE theo index.html Chào mừng bạn đến với Myproj

Cấu trúc thư mục cho Bước 4 và Bước 5 ở trên như sau

E:\myproj
    |--index.html
    |
    |--images
    |     |----myfoto.jpg
    |
    |--WEB-INF
    |     |--web.xml

Bước 6:
Bây giờ khởi động máy chủ tomcat apache

Bước 7:
mở trình duyệt và nhập url như sau

http://localhost:8080/myproj    

sau đó bạn hiển thị nội dung được cung cấp trong index.html

Bước 8:
Để truy cập Hình ảnh trong đĩa cứng cục bộ của bạn (bên ngoài webroot)

http://localhost:8080/myproj/images/myfoto.jpg

bạn có thể vui lòng gợi ý cho tôi, làm thế nào để làm điều tương tự cho các giá trị động. Ý tôi là tôi muốn ghi dữ liệu (xml) vào thư mục cục bộ của tôi hoặc và để đọc dữ liệu đó trong trang jsp của tôi. Có cách nào để ghi vào máy chủ được quản lý vào thư mục để tôi truy cập chúng bằng thủ tục trên không ??
Sudip7

mặc dù tôi có thể chạy tệp index.html đúng cách nhưng không hiển thị hình ảnh trong trình duyệt web
rogerwar

Bài viết sai của tôi đang hoạt động tốt. Tôi chỉ quên đặt / ở cuối E: / myproj Tôi đổi nó thành E: / myproj / và nó hoạt động tốt Cảm ơn @sbabamca
rogerwar 29/05/2015

Xin chào, Cảm ơn cho bài viết và nó rất hữu ích. Ở đây tôi muốn tải lên các tập tin thông qua giao diện đến thư mục cụ thể đó. Tôi muốn kích hoạt phương thức POST cho cùng. Bất cứ ai có thể xin vui lòng giúp tôi trên cùng.
vicky

6

Đây là câu chuyện từ nơi làm việc của tôi:
- Chúng tôi cố gắng tải lên nhiều hình ảnh và tệp tài liệu bằng Struts 1 và Tomcat 7.x.
- Chúng tôi cố gắng ghi các tệp đã tải lên vào hệ thống tệp, tên tệp và đường dẫn đầy đủ đến các bản ghi cơ sở dữ liệu.
- Chúng tôi cố gắng tách các thư mục tệp bên ngoài thư mục ứng dụng web . (*)

Giải pháp dưới đây khá đơn giản, hiệu quả cho yêu cầu (*):

Trong tệp META-INF/context.xmltệp có nội dung sau: (Ví dụ: ứng dụng của tôi chạy tại http://localhost:8080/ABC, ứng dụng / dự án của tôi có tên ABC). (đây cũng là nội dung đầy đủ của tập tin context.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/ABC" aliases="/images=D:\images,/docs=D:\docs"/>

(hoạt động với Tomcat phiên bản 7 trở lên)

Kết quả: Chúng tôi đã được tạo 2 bí danh. Ví dụ: chúng tôi lưu hình ảnh tại: D:\images\foo.jpg và xem từ liên kết hoặc sử dụng thẻ hình ảnh:

<img src="http://localhost:8080/ABC/images/foo.jsp" alt="Foo" height="142" width="142">

hoặc là

<img src="/images/foo.jsp" alt="Foo" height="142" width="142">

(Tôi sử dụng Netbeans 7.x, Netbeans dường như tự động tạo tệp WEB-INF\context.xml)



0

nếu bất cứ ai không thể giải quyết vấn đề của mình bằng câu trả lời được chấp nhận, thì hãy lưu ý những điều sau đây:

  1. không cần đề cập localhost:<port>với <img> srcthuộc tính.
  2. hãy chắc chắn rằng bạn đang chạy dự án này bên ngoài nhật thực, bởi vì nhật thực tự tạo context docBasemục nhập bên trong server.xmltệp cục bộ của nó .

0

Đọc InputStream của một tệp và ghi nó vào ServletOutputStreamđể gửi dữ liệu nhị phân đến máy khách.

  • Tệp cục bộ Bạn có thể đọc tệp trực tiếp bằng FileInputStream ('path / image.png') .
  • Tệp Mongo DataBase của bạn có thể nhận InputStream bằng GridFS .
@WebServlet("/files/URLStream")
public class URLStream extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public URLStream() {
        super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File source = new File("D:\\SVN_Commit.PNG");
        long start = System.nanoTime();

        InputStream image = new FileInputStream(source);

        /*String fileID = request.getParameter("id");
        System.out.println("Requested File ID : "+fileID);
        // Mongo DB GridFS - https://stackoverflow.com/a/33544285/5081877
        image = outputImageFile.getInputStream();*/

        if( image != null ) {
            BufferedInputStream bin = null;
            BufferedOutputStream bout = null;
            ServletOutputStream sos = response.getOutputStream();
            try {
                bin = new BufferedInputStream( image );
                bout = new BufferedOutputStream( sos );
                int ch =0; ;
                while((ch=bin.read())!=-1) {
                    bout.write(ch);
                }
            } finally {
                bin.close();
                image.close();
                bout.close();
                sos.close();
            }

        } else {
            PrintWriter writer = response.getWriter();
            writer.append("Something went wrong with your request.");
            System.out.println("Image not available.");
        }
        System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));
    }
}

Kết quả URL trực tiếp đến srcattibute.

<img src='http://172.0.0.1:8080/ServletApp/files/URLStream?id=5a575be200c117cc2500003b' alt="mongodb File"/>
<img src='http://172.0.0.1:8080/ServletApp/files/URLStream' alt="local file"/>

<video controls="controls" src="http://172.0.0.1:8080/ServletApp/files/URLStream"></video>

0

Nếu bạn muốn làm việc với JAX-RS (ví dụ REST EAS), hãy thử điều này:

@Path("/pic")
public Response get(@QueryParam("url") final String url) {
    String picUrl = URLDecoder.decode(url, "UTF-8");

    return Response.ok(sendPicAsStream(picUrl))
            .header(HttpHeaders.CONTENT_TYPE, "image/jpg")
            .build();
}

private StreamingOutput sendPicAsStream(String picUrl) {
    return output -> {
        try (InputStream is = (new URL(picUrl)).openStream()) {
            ByteStreams.copy(is, output);
        }
    };
}

sử dụng javax.ws.rs.core.Responsecom.google.common.io.ByteStreams


-1

Tôi đã làm nó thậm chí còn đơn giản hơn. Vấn đề: Một tệp CSS có các liên kết url đến thư mục img. Được 404.

Tôi đã xem url, http: // tomcatfolder: port / img / blablah.png , không tồn tại. Nhưng, điều đó thực sự đang chỉ đến ứng dụng ROOT trong Tomcat.

Vì vậy, tôi chỉ sao chép thư mục img từ ứng dụng web của mình vào ứng dụng ROOT đó. Làm!

Tất nhiên không được đề xuất cho sản xuất, nhưng đây là một ứng dụng dành cho công cụ nội bộ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.