Java 웹 애플리케이션에서 애플리케이션 서버 외부에서 정적 데이터를 제공하는 가장 간단한 방법


131

Tomcat에서 실행되는 Java 웹 응용 프로그램이 있습니다. 웹 UI와 응용 프로그램에서 생성 된 PDF 파일 모두에 표시되는 정적 이미지를로드하고 싶습니다. 또한 웹 UI를 통해 업로드하면 새로운 이미지가 추가 및 저장됩니다.

정적 데이터를 웹 컨테이너 내에 저장하지만 웹 컨테이너 외부에서 저장 및로드하여 두통을 유발 하여이 작업을 수행하는 것은 문제가되지 않습니다.

이 시점에서 정적 데이터를 제공하기 위해 Apache와 같은 별도의 웹 서버를 사용하지 않는 것이 좋습니다. 또한 이미지를 데이터베이스에 바이너리로 저장하는 것을 좋아하지 않습니다.

이미지 디렉토리를 웹 컨테이너 외부의 디렉토리를 가리키는 기호 링크로 만드는 것과 같은 제안을 보았지만이 방법은 Windows 및 * nix 환경에서 모두 작동합니까?

일부는 이미지 제공을 처리하기 위해 필터 또는 서블릿을 작성하도록 제안하지만 이러한 제안은이를 수행하는 방법에 대한 자세한 정보에 대한 포인터없이 매우 모호하고 고급입니다.

답변:


161

이미지 디렉토리를 웹 컨테이너 외부의 디렉토리를 가리키는 기호 링크로 만드는 것과 같은 제안을 보았지만이 방법은 Windows 및 * nix 환경에서 모두 작동합니까?

* nix 파일 시스템 경로 규칙을 준수하는 경우 (즉 /path/to/files, 에서와 같이 슬래시를 독점적으로 사용하는 경우 ) 추악한 File.separator문자열 연결 을 사용하지 않고도 Windows에서도 작동합니다 . 그러나이 명령이 호출 된 곳과 동일한 작업 디스크에서만 스캔됩니다. Tomcat이 설치 예를 들어, 경우에 따라서 C:다음 /path/to/files사실을 가리 것이다 C:\path\to\files.

파일이 모두 webapp 외부에 있고 Tomcat DefaultServlet이 처리하도록하려면 Tomcat에서 기본적으로해야 할 일은 태그 /conf/server.xml내부에 다음 Context 요소를 추가하는 것입니다 <Host>.

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

이 방법으로을 통해 액세스 할 수 있습니다 http://example.com/files/.... GlassFish / Payara 구성 예는 여기에서 , WildFly 구성 예는 여기 에서 찾을 수 있습니다 .

파일 읽기 / 쓰기를 직접 제어하려면 Servlet기본적으로 InputStream파일을 가져 와서 의 파일에 FileInputStream쓰는 파일을 작성해야 OutputStream합니다 HttpServletResponse.

응답 Content-Type에서 클라이언트가 제공된 파일과 연관시킬 응용 프로그램을 알 수 있도록 헤더를 설정해야 합니다. 그리고 Content-Length클라이언트가 다운로드 진행률을 계산할 수 있도록 헤더를 설정해야합니다 . 그렇지 않으면 알 수 없습니다. 다른 이름 으로 저장 대화 상자 를 원하면 Content-Disposition헤더를로 설정해야합니다 . 그렇지 않으면 클라이언트가 인라인 표시를 시도합니다. 마지막으로 파일 내용을 응답 출력 스트림에 씁니다.attachment

이러한 서블릿의 기본 예는 다음과 같습니다.

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

}

url-pattern예를 들어의에 매핑 된 경우 /files/*이를 호출 할 수 있습니다 http://example.com/files/image.png. 이 방법 DefaultServlet으로 기본 이미지 제공 등 의 요청보다 요청을 더 많이 제어 할 수 있습니다 if (!file.exists()) file = new File("/path/to/files", "404.gif"). 또한 SEO에 더 친숙하고 IE가 다른 이름으로 저장 중에 올바른 파일 이름을 선택하지 않기 때문에 request.getPathInfo()위의를 사용하는 것이 좋습니다 .request.getParameter()

데이터베이스에서 파일을 제공하기 위해 동일한 논리를 재사용 할 수 있습니다. 간단하게 교체 new FileInputStream()에 의해 ResultSet#getInputStream().

도움이 되었기를 바랍니다.

또한보십시오:


1
@SalutonMondo : 최소한의 노력으로 길.
BalusC

@BalusC, 나는 이것을 시도했다 : <Context docBase="/path/to/images" path="/images" />Windows에서는, webapps 폴더에 상대적인 경로 얻기 :C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV

Windows에서는 다음과 같아야합니다.<Context docBase="C:\tmp\" path="/images" />
ACV

그리고 HTTP Status 404 - /images/할 때 : http://localhost:8080/images/, 그러나 tmp작품 에서 특정 파일 :: http://localhost:8080/images/Tulips.jpgOK
ACV

이렇게하면 폴더 안에 이미지를 넣으면 서버를 다시 시작하지 않는 한 이미지 링크에 응답 404가 표시됩니다. 이유가 있습니까?
Mateus Viccari

9

이미지를 고정 경로 (예 : / var / images 또는 c : \ images)에 놓고 응용 프로그램 설정 (Settings.class에서 내 예제에 표시)에 설정을 추가 한 다음로드하면됩니다. HttpServlet당신의 것에서 와 같이 :

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

또는 이미지를 조작하려는 경우 :

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

html 코드는 <img src="imageServlet?imageName=myimage.png" />

물론 파일 확장자에 따라 "image / jpeg"와 같은 다른 컨텐츠 유형을 제공하는 것을 고려해야합니다. 또한 일부 캐싱을 제공해야합니다.

또한 너비 및 높이 매개 변수를 인수로 제공하고 image.getScaledInstance(w, h, Image.SCALE_SMOOTH성능을 고려 하여 )를 사용하여 이미지의 품질 재조정에이 서블릿을 사용할 수 있습니다 .


2
이를 위해 Java 2D API가 실제로 필요하지 않으며 불필요하게 더 많은 오버 헤드 만 추가됩니다. InputStream을 읽고 OutputStream에 쓰십시오.
BalusC

1
그렇다. 나는 rescaling과 다른 조작이라는 아이디어로 응답을 시작했지만 결국 단순화했다.
Bozho

7

server.xml에 추가하십시오.

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

web.xml에서 dir 파일 목록 매개 변수를 사용으로 설정하십시오.

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

web.xml의 변경으로 파일을 선택 상자로 보내려면 파일 목록을 얻을 수 있습니까?
Tomasz Waszczyk

1
이 변화는 바람둥이의 web.xml이 아닌 응용 프로그램에있을 것입니다
vsingh

감사합니다! web.xml에서 dir 파일 목록 매개 변수를 활성화해야합니까?
dariru

6

요구 사항 : WEBROOT 디렉토리 외부 또는 로컬 디스크에서 정적 리소스 (이미지 / 비디오 등)에 액세스

1 단계 :
Tomcat 서버의 webapps 아래에 폴더를 만듭니다., 폴더 이름이 myproj라고 가정 해 봅시다.

2 단계 :
myproj에서 WEB-INF 폴더를 작성하십시오.이 아래에 간단한 web.xml을 작성하십시오.

web.xml의 코드

<web-app>
</web-app>

위의 두 단계에 대한 디렉토리 구조

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

3 단계 :
이제 다음 위치에 이름이 myproj.xml 인 xml 파일을 작성하십시오.

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

myproj.xml의 코드 :

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

4 단계 :
4 A) 이제 하드 디스크의 E 드라이브에 이름이 myproj 인 폴더를 만들고 새 폴더를 만듭니다.

이름 이미지가있는 폴더 및 이미지 폴더에 일부 이미지 배치 (e:myproj\images\)

myfoto.jpg가 아래에 있다고 가정 해 봅시다. e:\myproj\images\myfoto.jpg

B) 이제 이름이 WEB-INF 인 폴더를 e:\myproj\WEB-INF만들고 WEB-INF 폴더에 web.xml을 만듭니다.

web.xml의 코드

<web-app>
</web-app>

5 단계 :
이름이 index.html 인 .html 문서를 작성하고 e : \ myproj 아래에 배치하십시오.

index.html의 코드 Myproj에 오신 것을 환영합니다

위 4 단계 및 5 단계의 디렉토리 구조는 다음과 같습니다.

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

6 단계 :
이제 Apache Tomcat 서버를 시작하십시오.

7 단계 :
브라우저를 열고 다음과 같이 URL을 입력하십시오.

http://localhost:8080/myproj    

그런 다음 index.html에 제공된 내용을 표시하십시오.

8 단계 :
로컬 하드 디스크 (webroot 외부)에서 이미지에 액세스

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

동적 값에 대해 동일한 작업을 수행하는 방법을 제안 해 주시겠습니까? 내 로컬 디렉토리에 데이터 (xml)를 작성하고 jsp 페이지에서 데이터를 읽고 싶습니다. 위의 절차를 사용하여 액세스 할 수 있도록 디렉토리에 관리되는 서버에 쓸 수있는 방법이 있습니까 ??
Sudip7

index.html 파일을 제대로 실행할 수는 있지만 웹 브라우저에는 이미지가 표시되지 않습니다
rogerwar

내 실수 게시물이 잘 작동하고 있습니다. E : / myproj 끝에 /를 넣는 것을 잊었습니다. 나는 이것을 E : / myproj /로 변경하고 작동합니다. Thanks @sbabamca
rogerwar

안녕하세요, 게시물 주셔서 감사합니다 그리고 그것은 매우 유용합니다. 여기서 인터페이스를 통해 해당 특정 디렉토리에 파일을 업로드하고 싶습니다. 동일한 POST 방법을 사용하고 싶습니다. 누구든지 똑같이 나를 도울 수 있습니까?
vicky

6

이것은 내 직장에서 온 이야기입니다.
-Struts 1 및 Tomcat 7.x를 사용하여 여러 이미지를 업로드하고 문서 파일을 업로드하려고합니다.
-업로드 된 파일을 파일 시스템, 파일 이름 및 데이터베이스 레코드의 전체 경로에 쓰려고합니다.
- 웹앱 디렉토리 외부에서 파일 폴더분리 하려고합니다 . (*)

아래 솔루션은 매우 간단하고 요구 사항에 효과적입니다 (*).

META-INF/context.xml내용이 다음과 같은 파일 파일에 있습니다 (예 : 내 응용 프로그램은에서 실행되고 http://localhost:8080/ABC내 응용 프로그램 / 프로젝트는 ABC). (이것은 또한 파일의 전체 내용입니다 context.xml)

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

(Tomcat 버전 7 이상에서 작동)

결과 : 2 개의 별칭이 생성되었습니다. 예를 들어 이미지를에 저장 D:\images\foo.jpg 하고 링크에서 보거나 이미지 태그를 사용하여 봅니다.

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

또는

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

(나는 Netbeans 7.x를 사용하고 Netbeans는 자동 파일 생성으로 보입니다 WEB-INF\context.xml)



0

수락 된 답변으로 문제를 해결할 수없는 사람은 다음 사항을 고려하십시오.

  1. 언급 할 필요없이 localhost:<port><img> src속성을.
  2. 이클립스가 context docBase로컬 server.xml파일 내부에 자체적으로 항목을 작성하므로 이클립스 외부에서이 프로젝트를 실행 중인지 확인하십시오 .

0

ServletOutputStream이진 데이터를 클라이언트에 보내기 위해 파일의 InputStream을 읽고 씁니다 .

@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));
    }
}

결과를 직접 URL로 지정하십시오 src.

<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

JAX-RS (예 : RESTEasy) 로 작업하려면 다음을 시도하십시오.

@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);
        }
    };
}

사용 javax.ws.rs.core.Response하여com.google.common.io.ByteStreams


-1

나는 심지어 더 간단했다. 문제 : CSS 파일에 img 폴더에 대한 URL 링크가 있습니다. 404를 가져옵니다.

나는 URL을 보았다 존재하지 않는 http : // tomcatfolder : port / img / blablah.png을 보았습니다 . 그러나 그것은 실제로 Tomcat의 ROOT 앱을 가리키고 있습니다.

방금 웹 응용 프로그램에서 img 폴더를 해당 ROOT 응용 프로그램으로 복사했습니다. 공장!

물론 프로덕션에는 권장되지 않지만 내부 도구 개발 앱에 적합합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.