pengantar
Untuk menelusuri dan memilih file untuk diunggah, Anda perlu <input type="file">
bidang HTML dalam formulir. Seperti yang dinyatakan dalam spesifikasi HTML Anda harus menggunakan POST
metode dan enctype
atribut formulir harus diatur "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>
Setelah mengirimkan formulir seperti itu, data formulir multi-biner tersedia di badan permintaan dalam format yang berbeda dari ketika enctype
tidak diatur.
Sebelum Servlet 3.0, Servlet API tidak mendukung secara asli multipart/form-data
. Ini hanya mendukung bentuk standar dari application/x-www-form-urlencoded
. The request.getParameter()
dan permaisuri akan semua kembali null
bila menggunakan data formulir multi. Di sinilah FileUpload Apache Commons yang terkenal muncul.
Jangan parsing secara manual!
Secara teori Anda dapat menguraikan tubuh permintaan sendiri berdasarkan ServletRequest#getInputStream()
. Namun, ini adalah pekerjaan yang tepat dan membosankan yang membutuhkan pengetahuan yang akurat tentang RFC2388 . Anda tidak boleh mencoba melakukan ini sendiri atau menyalin beberapa kode pustaka buatan sendiri yang ditemukan di tempat lain di Internet. Banyak sumber online gagal dalam hal ini, seperti roseindia.net. Lihat juga mengunggah file pdf . Anda sebaiknya menggunakan perpustakaan asli yang digunakan (dan diuji secara implisit!) Oleh jutaan pengguna selama bertahun-tahun. Perpustakaan seperti itu telah membuktikan kekokohannya.
Saat Anda sudah menggunakan Servlet 3.0 atau yang lebih baru, gunakan API asli
Jika Anda menggunakan setidaknya Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, dll), maka Anda dapat menggunakan API standar yang disediakan HttpServletRequest#getPart()
untuk mengumpulkan item data formulir multi-individu (kebanyakan implementasi Servlet 3.0 sebenarnya menggunakan Apache Commons FileUpload di bawah sampul untuk ini!). Juga, bidang formulir normal tersedia dengan getParameter()
cara biasa.
Pertama, @MultipartConfig
beri anotasi pada servlet Anda agar dapat mengenali dan mendukung multipart/form-data
permintaan dan mulai getPart()
bekerja:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Kemudian, implementasikan doPost()
sebagai 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)
}
Perhatikan Path#getFileName()
. Ini adalah perbaikan MSIE untuk mendapatkan nama file. Browser ini salah mengirim jalur file lengkap di sepanjang nama, bukan hanya nama file.
Jika Anda memiliki <input type="file" name="file" multiple="true" />
unggahan multi-file, kumpulkan sebagai berikut (sayangnya tidak ada metode seperti itu request.getParts("file")
):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
for (Part filePart : fileParts) {
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
}
Saat Anda belum menggunakan Servlet 3.1, dapatkan nama file yang dikirimkan secara manual
Catatan yang Part#getSubmittedFileName()
diperkenalkan di Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, dll). Jika Anda belum menggunakan Servlet 3.1, maka Anda memerlukan metode utilitas tambahan untuk mendapatkan nama file yang dikirimkan.
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;
}
String fileName = getSubmittedFileName(filePart);
Perhatikan perbaikan MSIE untuk mendapatkan nama file. Browser ini salah mengirim jalur file lengkap di sepanjang nama, bukan hanya nama file.
Saat Anda belum menggunakan Servlet 3.0, gunakan Apache Commons FileUpload
Jika Anda belum menggunakan Servlet 3.0 (bukankah ini saatnya untuk memutakhirkan?), Praktik umum adalah menggunakan Apache Commons FileUpload untuk menguraikan permintaan data formulir multi-bagian. Ini memiliki Panduan Pengguna dan FAQ yang sangat baik (hati-hati melalui keduanya). Ada juga O'Reilly (" cos ") MultipartRequest
, tetapi ia memiliki beberapa bug (minor) dan tidak aktif lagi selama bertahun-tahun. Saya tidak akan merekomendasikan menggunakannya. Apache Commons FileUpload masih dipelihara secara aktif dan saat ini sangat matang.
Untuk menggunakan FileUpload Apache Commons, Anda harus memiliki setidaknya file berikut di aplikasi web Anda /WEB-INF/lib
:
Upaya awal Anda kemungkinan besar gagal karena Anda lupa IO milik bersama.
Berikut ini contoh kickoff bagaimana tampilan doPost()
Anda UploadServlet
saat menggunakan Apache Commons FileUpload:
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);
}
// ...
}
Ini sangat penting bahwa Anda tidak menelepon getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
, dll pada permintaan yang sama sebelumnya. Jika tidak, wadah servlet akan membaca dan mem-parsing badan permintaan dan dengan demikian Apache Commons FileUpload akan mendapatkan badan permintaan kosong. Lihat juga ao ServletFileUpload # parseRequest (request) mengembalikan daftar kosong .
Perhatikan FilenameUtils#getName()
. Ini adalah perbaikan MSIE untuk mendapatkan nama file. Browser ini salah mengirim jalur file lengkap di sepanjang nama, bukan hanya nama file.
Atau Anda juga dapat membungkus semua ini dengan cara Filter
mem-parsing semuanya secara otomatis dan memasukkan barang-barang itu kembali ke dalam parametermap permintaan sehingga Anda dapat terus menggunakan request.getParameter()
cara biasa dan mengambil file yang diunggah request.getAttribute()
. Anda dapat menemukan contoh di artikel blog ini .
Solusi untuk bug GlassFish3 getParameter()
masih kembalinull
Perhatikan bahwa versi Glassfish yang lebih tua dari 3.1.2 memiliki bug di mana getParameter()
masih kembali null
. Jika Anda menargetkan wadah seperti itu dan tidak dapat memutakhirkannya, maka Anda perlu mengekstraksi nilainya getPart()
dengan bantuan metode utilitas ini:
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();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
Menyimpan file yang diunggah (jangan gunakan getRealPath()
juga part.write()
!)
Buka jawaban berikut untuk perincian tentang cara menyimpan dengan benar yang diperoleh InputStream
( fileContent
variabel seperti yang ditunjukkan dalam cuplikan kode di atas) ke disk atau basis data:
Melayani file yang diunggah
Buka jawaban berikut untuk detail tentang penyajian file yang disimpan dengan benar dari disk atau database kembali ke klien:
Ajaxifying formulir
Buka jawaban berikut cara mengunggah menggunakan Ajax (dan jQuery). Perhatikan bahwa kode servlet untuk mengumpulkan data formulir tidak perlu diubah untuk ini! Hanya cara Anda merespons yang dapat diubah, tetapi ini agak sepele (yaitu alih-alih meneruskan ke JSP, cukup cetak beberapa JSON atau XML atau bahkan teks biasa tergantung pada apa pun yang diharapkan skrip yang bertanggung jawab atas panggilan Ajax).
Semoga ini semua membantu :)