Rumah > Java > teks badan

Bagaimana untuk memuat naik fail ke pelayan menggunakan JSP/Servlet?

王林
Lepaskan: 2024-02-22 12:40:07
ke hadapan
796 orang telah melayarinya

Editor PHP Youzi akan memperkenalkan kepada anda cara menggunakan JSP/Servlet untuk memuat naik fail ke pelayan di Java. Dalam pembangunan Web, muat naik fail adalah keperluan biasa, dan JSP/Servlet boleh digunakan untuk melaksanakan fungsi muat naik fail yang mudah dan cekap. Seterusnya, kami akan memperkenalkan secara terperinci langkah-langkah memuat naik fail ke pelayan melalui kod Java, supaya anda boleh menguasai kemahiran ini dengan cepat dan meningkatkan keupayaan pembangunan anda.

Kandungan soalan

Bagaimana untuk menggunakan jsp/servlet untuk memuat naik fail ke pelayan?

Saya mencuba ini:

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>
Salin selepas log masuk

Namun, saya hanya mendapat nama fail, bukan kandungan fail. Bila saya akan enctype="multipart/form-data" 添加到 <form> 时,request.getparameter() 返回 null.

Semasa penyelidikan saya, saya terjumpa apache common fileupload. Saya mencuba ini:

fileitemfactory factory = new diskfileitemfactory();
servletfileupload upload = new servletfileupload(factory);
list items = upload.parserequest(request); // this line is where it died.
Salin selepas log masuk

Malangnya, servlet melemparkan pengecualian tanpa mesej atau alasan yang jelas. Berikut ialah jejak tindanan:

SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:637)
Salin selepas log masuk

Penyelesaian

Pengenalan

Untuk menyemak imbas dan memilih fail untuk dimuat naik, anda perlu menambah html pada borang <input type="file"> 字段。如 HTML specification 中所述,您必须使用 post 方法,并且表单的 enctype 属性必须设置为 "multipart/form-data".

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>
Salin selepas log masuk

Selepas menyerahkan borang sedemikian, data borang berbilang bahagian binari tersedia dalam badan permintaan dalam enctypeformat yang berbeza berbanding apabila tidak ditetapkan.

Sebelum servlet 3.0 (Disember 2009), servlet api itu sendiri tidak menyokong multipart/form-data。它仅支持默认形式 enctype application/x-www-form-urlencoded。当使用多部分表单数据时,request.getparameter() 和 consorts 都将返回 null. Di sinilah Apache Commons FileUpload yang terkenal masuk.

Jangan menghuraikannya secara manual!

Secara teorinya anda boleh menghuraikan sendiri badan permintaan berdasarkan ServletRequest#getInputStream(). Walau bagaimanapun, ini adalah tugas yang tepat dan membosankan yang memerlukan pengetahuan yang tepat tentang RFC2388. Anda tidak sepatutnya cuba melakukan ini sendiri atau menyalin-tampal beberapa kod tanpa perpustakaan buatan sendiri yang terdapat di tempat lain di Internet. Banyak sumber dalam talian gagal dalam hal ini, seperti roseindia.net. Lihat juga memuat naik fail pdf. Anda harus menggunakan perpustakaan sebenar yang telah digunakan (dan diuji secara tersirat!) oleh berjuta-juta pengguna selama bertahun-tahun. Perpustakaan sedemikian telah membuktikan kemantapannya.

Apabila anda sudah menggunakan servlet 3.0 atau lebih tinggi, sila gunakan api asli

Jika anda menggunakan sekurang-kurangnya servlet 3.0 (tomcat 7, jetty 9, jboss as 6, glassfish 3, dsb., ia telah wujud sejak 2010) maka anda boleh menggunakan HttpServletRequest#getPart() HttpServletRequest#getPart() 提供的标准 api 来收集单独的多部分表单数据项(大多数 servlet 3.0 实现实际上都在幕后使用 apache commons fileupload!)。此外,正常形式字段可以通过 getparameter() Menyediakan API standard untuk mengumpul item data borang berbilang bahagian individu (kebanyakan pelaksanaan servlet 3.0 sebenarnya menggunakan Apache Commons FileUpload di belakang tabir!). Selain itu, medan borang biasa boleh diperoleh dengan cara biasa melalui getparameter().

Mula-mula anotasi servlet anda dengan @MultipartConfig@MultipartConfig 注释您的 servlet,以便让它识别并支持 multipart/form-data 请求,从而使 getpart() supaya ia mengenali dan menyokong permintaan multipart/form-data, sekali gus membolehkan getpart()</code > Working biasanya: </p> <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">@webservlet(&quot;/upload&quot;) @multipartconfig public class uploadservlet extends httpservlet { // ... }</pre><div class="contentsignin">Salin selepas log masuk</div></div> <p>Kemudian, laksanakan <code>dopost() seperti berikut:

protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
    string description = request.getparameter("description"); // retrieves <input type="text" name="description">
    part filepart = request.getpart("file"); // retrieves <input type="file" name="file">
    string filename = paths.get(filepart.getsubmittedfilename()).getfilename().tostring(); // msie fix.
    inputstream filecontent = filepart.getinputstream();
    // ... (do your job here)
}
Salin selepas log masuk

Perhatian path#getfilename(). Ini ialah pembetulan msie mengenai mendapatkan nama fail. Penyemak imbas ini salah menghantar laluan dan nama fail penuh dan bukannya hanya nama fail.

Jika anda ingin memuat naik berbilang fail melalui multiple="true",

<input type="file" name="files" multiple="true" />
Salin selepas log masuk

Atau cara lama dengan pelbagai input,

<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
...
Salin selepas log masuk

Anda kemudian boleh mengumpulnya seperti berikut (malangnya tiada kaedah seperti request.getparts("files")):

protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
    // ...
    list<part> fileparts = request.getparts().stream().filter(part -> "files".equals(part.getname()) && part.getsize() > 0).collect(collectors.tolist()); // retrieves <input type="file" name="files" multiple="true">

    for (part filepart : fileparts) {
        string filename = paths.get(filepart.getsubmittedfilename()).getfilename().tostring(); // msie fix.
        inputstream filecontent = filepart.getinputstream();
        // ... (do your job here)
    }
}
Salin selepas log masuk

当您尚未使用 servlet 3.1 时,手动获取提交的文件名

请注意,Part#getSubmittedFileName() 是在 servlet 3.1 中引入的(tomcat 8、jetty 9、wildfly 8、glassfish 4 等,它们自 2013 年以来就已存在)。如果您还没有使用 servlet 3.1(真的吗?),那么您需要一个额外的实用方法来获取提交的文件名。

private static string getsubmittedfilename(part part) {
    for (string cd : part.getheader("content-disposition").split(";")) {
        if (cd.trim().startswith("filename")) {
            string filename = cd.substring(cd.indexof('=') + 1).trim().replace("\"", "");
            return filename.substring(filename.lastindexof('/') + 1).substring(filename.lastindexof('\\') + 1); // msie fix.
        }
    }
    return null;
}
Salin selepas log masuk
string filename = getsubmittedfilename(filepart);
Salin selepas log masuk

请注意有关获取文件名的 msie 修复。此浏览器错误地发送完整文件路径和名称,而不仅仅是文件名。

当您尚未使用 servlet 3.0 时,请使用 apache commons fileupload

如果您还没有使用 servlet 3.0(是不是该升级了?它已经十多年前发布了!),通常的做法是使用 Apache Commons FileUpload 来解析多部分表单数据请求。它有一个很好的User GuideFAQ(仔细检查两者)。还有 o'reilly(“cos”)multipartrequest,但它有一些(小)错误,并且多年来不再积极维护。我不建议使用它。 apache commons fileupload 仍在积极维护,目前非常成熟。

为了使用 apache commons fileupload,您的 web 应用程序的 /web-inf/lib 中至少需要有以下文件:

您最初的尝试失败很可能是因为您忘记了公共 io。

下面是一个启动示例,显示使用 apache commons fileupload 时 uploadservletdopost() 的样子:

protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
    try {
        list<fileitem> items = new servletfileupload(new diskfileitemfactory()).parserequest(request);
        for (fileitem item : items) {
            if (item.isformfield()) {
                // process regular form field (input type="text|radio|checkbox|etc", select, etc).
                string fieldname = item.getfieldname();
                string fieldvalue = item.getstring();
                // ... (do your job here)
            } else {
                // process form file field (input type="file").
                string fieldname = item.getfieldname();
                string filename = filenameutils.getname(item.getname());
                inputstream filecontent = item.getinputstream();
                // ... (do your job here)
            }
        }
    } catch (fileuploadexception e) {
        throw new servletexception("cannot parse multipart request.", e);
    }

    // ...
}
Salin selepas log masuk

非常重要的是,您不要事先在同一请求上调用 getparameter()getparametermap()getparametervalues()getinputstream()getreader() 等。否则,servlet 容器将读取并解析请求正文,因此 apache commons fileupload 将得到一个空请求正文。另请参见 a.o. ServletFileUpload#parseRequest(request) returns an empty list

注意 filenameutils#getname()。这是有关获取文件名的 msie 修复。此浏览器错误地发送完整文件路径和名称,而不仅仅是文件名。

或者,您也可以将这一切包装在 filter 中,它会自动解析所有内容并将内容放回请求的参数映射中,以便您可以继续以通常的方式使用 request.getparameter() 并通过以下方式检索上传的文件request.getattribute()You can find an example in this blog article

glassfish3 中 getparameter() 错误的解决方法仍然返回 null

请注意,早于 3.1.2 的 glassfish 版本有 a bug,其中 getparameter() 仍返回 null。如果您的目标是这样的容器并且无法升级它,那么您需要借助此实用程序方法从 getpart() 中提取值:

private static string getvalue(part part) throws ioexception {
    bufferedreader reader = new bufferedreader(new inputstreamreader(part.getinputstream(), "utf-8"));
    stringbuilder value = new stringbuilder();
    char[] buffer = new char[1024];
    for (int length = 0; (length = reader.read(buffer)) > 0;) {
        value.append(buffer, 0, length);
    }
    return value.tostring();
}
Salin selepas log masuk
string description = getvalue(request.getpart("description")); // retrieves <input type="text" name="description">
Salin selepas log masuk

保存上传的文件(不要使用 getrealpath()part.write()!)

前往以下答案,详细了解如何将获得的 inputstream (上述代码片段中所示的 filecontent 变量)正确保存到磁盘或数据库:

提供上传的文件

请参阅以下答案,了解如何正确地将保存的文件从磁盘或数据库返回给客户端的详细信息:

ajax 表单

前往以下答案,了解如何使用 ajax(和 jquery)上传。请注意,无需为此更改用于收集表单数据的 servlet 代码!只有您响应的方式可能会改变,但这相当简单(即,不转发到 jsp,只需打印一些 json 或 xml 甚至纯文本,具体取决于负责 ajax 调用的脚本所期望的内容)。

如果您碰巧使用 Spring MVC,请按以下方法操作(我将其留在这里,以防有人发现它有用):

使用 enctype 属性设置为“multipart/form-datazqbenczqb”的表单(与 <a href="https://www.php.cn/link/8a13dab3f5ec9e22d0d1495c8c85e436">BalusC's answer</a> 相同):</p> <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false;">&lt;form action=&quot;upload&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt; &lt;input type=&quot;file&quot; name=&quot;file&quot; /&gt; &lt;input type=&quot;submit&quot; value=&quot;upload&quot;/&gt; &lt;/form&gt; </pre><div class="contentsignin">Salin selepas log masuk</div></div> <p>在您的控制器中,将请求参数 <code>file 映射到 multipartfile 类型,如下所示:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
            byte[] bytes = file.getBytes(); // alternatively, file.getInputStream();
            // application logic
    }
}
Salin selepas log masuk

您可以使用 multipartfilegetoriginalfilename()getsize() 获取文件名和大小。

我已经使用 spring 版本 4.1.1.release 对此进行了测试。

Atas ialah kandungan terperinci Bagaimana untuk memuat naik fail ke pelayan menggunakan JSP/Servlet?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan