วิธีที่ง่ายที่สุดในการให้บริการข้อมูลคงที่จากภายนอกแอ็พพลิเคชันเซิร์ฟเวอร์ในเว็บแอ็พพลิเคชัน Java


131

ฉันมีเว็บแอปพลิเคชัน Java ที่ทำงานบน Tomcat ฉันต้องการโหลดภาพนิ่งที่จะแสดงทั้งบน UI ของเว็บและในไฟล์ PDF ที่แอปพลิเคชันสร้างขึ้น นอกจากนี้ภาพใหม่จะถูกเพิ่มและบันทึกโดยการอัพโหลดผ่าน Web UI

การทำเช่นนี้ไม่ใช่ปัญหาโดยการจัดเก็บข้อมูลแบบคงที่ไว้ในคอนเทนเนอร์ของเว็บ แต่การจัดเก็บและโหลดข้อมูลจากภายนอกเว็บคอนเทนเนอร์ทำให้ฉันปวดหัว

ฉันไม่ต้องการใช้เว็บเซิร์ฟเวอร์แยกต่างหากเช่น Apache เพื่อให้บริการข้อมูลคงที่ ณ จุดนี้ ฉันไม่ชอบแนวคิดในการจัดเก็บภาพเป็นไบนารีในฐานข้อมูล

ฉันเคยเห็นคำแนะนำบางอย่างเช่นการให้ไดเรกทอรีรูปภาพเป็นลิงก์สัญลักษณ์ที่ชี้ไปยังไดเร็กทอรีนอกคอนเทนเนอร์ของเว็บ แต่วิธีนี้จะใช้ได้ทั้งในสภาพแวดล้อม Windows และ * nix หรือไม่

บางคนแนะนำให้เขียนฟิลเตอร์หรือ servlet สำหรับจัดการการแสดงรูปภาพ แต่คำแนะนำเหล่านั้นคลุมเครือและอยู่ในระดับสูงโดยไม่มีคำแนะนำถึงข้อมูลโดยละเอียดเพิ่มเติมเกี่ยวกับวิธีดำเนินการนี้

คำตอบ:


161

ฉันเคยเห็นคำแนะนำบางอย่างเช่นการให้ไดเรกทอรีรูปภาพเป็นลิงก์สัญลักษณ์ที่ชี้ไปยังไดเร็กทอรีนอกคอนเทนเนอร์ของเว็บ แต่วิธีนี้จะใช้ได้ทั้งในสภาพแวดล้อม Windows และ * nix หรือไม่

หากคุณปฏิบัติตามกฎเส้นทางระบบไฟล์ * nix (กล่าวคือคุณใช้เครื่องหมายทับไปข้างหน้าโดยเฉพาะ/path/to/files) มันก็จะทำงานบน Windows ได้เช่นกันโดยไม่จำเป็นต้องยุ่งเกี่ยวกับการต่อFile.separatorสายอักขระที่น่าเกลียด อย่างไรก็ตามมันจะถูกสแกนบนดิสก์ที่ใช้งานเดียวกับที่เรียกใช้คำสั่งนี้ ดังนั้นหาก Tomcat เป็นตัวอย่างที่ติดตั้งบนC:แล้วจริงจะชี้ไปที่/path/to/filesC:\path\to\files

หากไฟล์ทั้งหมดอยู่นอก webapp และคุณต้องการให้ Tomcat DefaultServletจัดการไฟล์เหล่านั้นสิ่งที่คุณต้องทำโดยทั่วไปใน Tomcat ก็คือการเพิ่มองค์ประกอบบริบทต่อไปนี้/conf/server.xmlใน<Host>แท็กภายใน:

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

http://example.com/files/...วิธีนี้พวกเขาจะสามารถเข้าถึงได้ผ่าน ตัวอย่างเช่นการกำหนดค่า GlassFish / ปลาสคอมบิรอยด์สามารถพบได้ที่นี่และเป็นตัวอย่างการกำหนดค่า WildFly สามารถพบได้ที่นี่

หากคุณต้องการควบคุมการอ่าน / เขียนไฟล์ด้วยตัวเองคุณต้องสร้างสิ่งServletนี้ซึ่งโดยพื้นฐานแล้วจะได้รับInputStreamไฟล์ในรูปแบบของตัวอย่างเช่นFileInputStreamและเขียนลงในOutputStreamไฟล์HttpServletResponse.

ในการตอบกลับคุณควรตั้งค่าContent-Typeส่วนหัวเพื่อให้ไคลเอนต์ทราบว่าจะเชื่อมโยงแอปพลิเคชันใดกับไฟล์ที่ให้มา และคุณควรตั้งค่าContent-Lengthส่วนหัวเพื่อให้ไคลเอนต์สามารถคำนวณความคืบหน้าในการดาวน์โหลดมิฉะนั้นจะไม่ทราบ และคุณควรตั้งค่าContent-Dispositionส่วนหัวเป็นattachmentถ้าคุณต้องการกล่องโต้ตอบบันทึกเป็นมิฉะนั้นไคลเอนต์จะพยายามแสดงในบรรทัด สุดท้ายเพียงแค่เขียนเนื้อหาไฟล์ไปยังสตรีมเอาต์พุตการตอบสนอง

นี่คือตัวอย่างพื้นฐานของ servlet ดังกล่าว:

@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")หรือมากกว่านั้น) นอกจากนี้ยังใช้request.getPathInfo()เป็นที่ต้องการเหนือrequest.getParameter()เพราะมันเป็นมิตร SEO เพิ่มเติมและอื่น ๆ IE จะได้รับชื่อไฟล์ที่ถูกต้องในระหว่างการบันทึกเป็น

คุณสามารถใช้ตรรกะเดียวกันนี้ซ้ำเพื่อให้บริการไฟล์จากฐานข้อมูล เพียงแทนที่โดย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.jpgเป็น OK
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" เช่นตามนามสกุลไฟล์ นอกจากนี้คุณควรจัดเตรียมแคชไว้ด้วย

นอกจากนี้คุณยังสามารถใช้ servlet นี้สำหรับการปรับขนาดรูปภาพของคุณได้อย่างมีคุณภาพโดยระบุพารามิเตอร์ความกว้างและความสูงเป็นอาร์กิวเมนต์และใช้image.getScaledInstance(w, h, Image.SCALE_SMOOTH) โดยพิจารณาจากประสิทธิภาพ


2
คุณไม่จำเป็นต้องใช้ Java 2D API สำหรับสิ่งนี้มันจะเพิ่มค่าใช้จ่ายเพิ่มเติมโดยไม่จำเป็นเท่านั้น เพียงแค่อ่าน InputStream และเขียนลงใน OutputStream
BalusC

1
ใช่ฉันเริ่มต้นการตอบสนองด้วยแนวคิดในการปรับขนาดและการปรับเปลี่ยนอื่น ๆ แต่สุดท้ายก็ทำให้มันง่ายขึ้น
Bozho

7

เพิ่มใน server.xml:

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

เปิดใช้งานพารามิเตอร์รายการไฟล์ dir ใน web.xml:

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

ด้วยการเปลี่ยนแปลงใน web.xml ฉันจะได้รับรายชื่อไฟล์เพื่อส่งไปยังกล่องเลือก?
Tomasz Waszczyk

1
การเปลี่ยนแปลงนี้จะอยู่ใน web.xml ของ tomcat ไม่ใช่แอปพลิเคชันของคุณ
vsingh

ขอบคุณสำหรับสิ่งนี้! การเปิดใช้พารามิเตอร์การแสดงรายการไฟล์ dir ใน web.xml จำเป็นหรือไม่
dariru

6

ข้อกำหนด: การเข้าถึงทรัพยากรแบบคงที่ (รูปภาพ / วิดีโอ ฯลฯ ) จากภายนอกไดเรกทอรี WEBROOT หรือจากดิสก์ภายในเครื่อง

ขั้นตอนที่ 1:
สร้างโฟลเดอร์ภายใต้ webapps ของเซิร์ฟเวอร์ tomcat ให้เราพูดว่าชื่อโฟลเดอร์คือ myproj

ขั้นตอนที่ 2:
ภายใต้ myproj สร้างโฟลเดอร์ WEB-INF ภายใต้สิ่งนี้ให้สร้าง web.xml อย่างง่าย

รหัสภายใต้ web.xml

<web-app>
</web-app>

โครงสร้างไดเรกทอรีสำหรับสองขั้นตอนข้างต้น

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

ขั้นตอนที่ 3:
ตอนนี้สร้างไฟล์ xml ด้วยชื่อ myproj.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) ตอนนี้สร้างโฟลเดอร์ที่มีชื่อ myproj ในไดรฟ์ E ของฮาร์ดดิสก์ของคุณและสร้างไฟล์

โฟลเดอร์ที่มีชื่อภาพและวางภาพบางภาพในโฟลเดอร์ภาพ (e:myproj\images\)

ให้เราสมมติว่า myfoto.jpg อยู่ข้างใต้ e:\myproj\images\myfoto.jpg

4 B) ตอนนี้สร้างโฟลเดอร์ที่มีชื่อ WEB-INF e:\myproj\WEB-INFและสร้าง web.xml ในโฟลเดอร์ WEB-INF

โค้ดใน web.xml

<web-app>
</web-app>

ขั้นตอนที่ 5:
ตอนนี้สร้างเอกสาร. html ด้วยชื่อ index.html และวางไว้ใต้ e: \ myproj

CODE ภายใต้ 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 / และมันใช้งานได้ดีขอบคุณ @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. ตรวจสอบให้แน่ใจว่าคุณกำลังรันโปรเจ็กต์นี้นอก eclipse เนื่องจาก eclipse สร้างcontext docBaseรายการด้วยตัวเองภายในโลคัลserver.xmlไฟล์

0

อ่าน InputStream ของไฟล์และเขียนเพื่อServletOutputStreamส่งข้อมูลไบนารีไปยังไคลเอนต์

  • ไฟล์ท้องถิ่นคุณสามารถอ่านไฟล์ได้โดยตรงโดยใช้FileInputStream ( 'เส้นทาง / image.png')
  • Mongo ไฟล์ฐานข้อมูลคุณสามารถได้รับ InputStream ใช้ 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));
    }
}

ส่งผลลัพธ์ URL โดยตรงไปยัง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

หากคุณต้องการทำงานกับ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 มีลิงก์ url ไปยังโฟลเดอร์ img ได้รับ 404

ฉันดู url, http: // tomcatfolder: port / img / blablah.pngซึ่งไม่มีอยู่ แต่นั่นชี้ไปที่แอป ROOT ใน Tomcat จริงๆ

ดังนั้นฉันจึงคัดลอกโฟลเดอร์ img จากเว็บแอปไปยังแอป ROOT นั้น Works!

ไม่แนะนำให้ใช้ในการผลิตแน่นอน แต่สำหรับแอปพัฒนาเครื่องมือภายใน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.