Giriş
Yüklenecek bir dosyaya göz atmak ve seçmek <input type="file">
için formda bir HTML alanına ihtiyacınız vardır . HTML spesifikasyonunda belirtildiği gibi , POST
yöntemi kullanmanız ve enctype
formun niteliğinin ayarlanması gerekir "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>
Bu tür bir form gönderdikten sonra, ikili çok parçalı bir şekilde veri istek vücutta mevcut olan farklı bir biçimde olduğundan dahaenctype
ayarlanmadı.
Servlet 3.0'dan önce, Servlet API'si yerel olarak desteklemiyordu multipart/form-data
. Yalnızca varsayılan form şifrelemesini destekler application/x-www-form-urlencoded
. request.getParameter()
Ve eşleri tüm iade ediyorum null
çok parçalı form verilerini kullanırken. Tanınmış Apache Commons FileUpload burada devreye giriyor.
Elle ayrıştırmayın!
Teorik olarak, istek gövdesini kendinize dayalı olarak ayrıştırabilirsiniz ServletRequest#getInputStream()
. Ancak, bu RFC2388 hakkında kesin bilgi gerektiren hassas ve sıkıcı bir çalışmadır . Bunu kendi başınıza yapmaya çalışmamalı veya İnternet'in başka bir yerinde bulunan kütüphaneden bağımsız bazı kodları kopyalamaya çalışmamalısınız. Birçok çevrimiçi kaynak bu konuda çok başarısız oldu, örneğin roseindia.net. Ayrıca bkz . Pdf dosyası yükleme . Milyonlarca kullanıcı tarafından yıllarca kullanılan (ve dolaylı olarak test edilen!) Gerçek bir kütüphane kullanmayı tercih etmelisiniz. Böyle bir kütüphane sağlamlığını kanıtlamıştır.
Zaten Servlet 3.0 veya daha yeni bir sürümdeyseniz, yerel API kullanın
En az Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, vb.) Kullanıyorsanız HttpServletRequest#getPart()
, tek tek çok parçalı form veri öğelerini toplamak için sağlanan standart API'yi kullanabilirsiniz (çoğu Servlet 3.0 uygulaması aslında Apache kullanır Bunun için kapakların altında Commons FileUpload!). Ayrıca, normal form alanları getParameter()
normal şekilde kullanılabilir.
İlk önce sunucu uygulamanıza, istekleri @MultipartConfig
tanıması ve desteklemesi multipart/form-data
ve böylece getPart()
çalışmaya başlaması için açıklama ekleyin :
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Ardından, doPost()
aşağıdaki gibi uygulayın :
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)
}
Not edin Path#getFileName()
. Bu dosya adını almak için bir MSIE düzeltmedir. Bu tarayıcı, yalnızca dosya adı yerine tam dosya yolunu ad boyunca yanlış gönderir.
Eğer bir var <input type="file" name="file" multiple="true" />
, çoklu dosya yükleme için aşağıda onları toplamak (maalesef böyle bir yöntem olarak yoktur 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)
}
}
Henüz Servlet 3.1'de değilseniz, gönderilen dosya adını el ile alın
Not Part#getSubmittedFileName()
Servlet 3.1 (Tomcat'e 8, iskelesi 9, WildFly 8, GlassFish'in 4, vs) tanıtıldı. Henüz Servlet 3.1'de değilseniz, gönderilen dosya adını almak için ek bir yardımcı program yöntemine ihtiyacınız vardır.
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);
MSIE düzeltmesini, dosya adını alma konusunda not alın. Bu tarayıcı, yalnızca dosya adı yerine tam dosya yolunu ad boyunca yanlış gönderir.
Henüz Servlet 3.0'da değilseniz, Apache Commons FileUpload'u kullanın
Henüz Servlet 3.0'da değilseniz (yükseltme zamanı gelmedi mi?), Yaygın uygulama, çok parçalı form veri isteklerini ayrıştırmak için Apache Commons FileUpload'u kullanmaktır . Mükemmel bir Kullanım Kılavuzu ve SSS (dikkatle her ikisini de gözden geçirin ) vardır. Ayrıca O'Reilly (" cos ") var MultipartRequest
, ancak bazı (küçük) hatalar var ve yıllarca aktif olarak tutulmuyor. Bunu kullanmanızı tavsiye etmem. Apache Commons FileUpload hala aktif olarak bakımını yapıyor ve şu anda çok olgun.
Apache Commons FileUpload'u kullanmak için web uygulamanızda en azından aşağıdaki dosyalara sahip olmanız gerekir /WEB-INF/lib
:
Müşterek IO'yu unuttuğunuz için ilk denemeniz büyük olasılıkla başarısız oldu.
İşte nasıl bir başlama örnek doPost()
, aramalarınızdan UploadServlet
Apache Commons FileUpload kullanırken gibi görünebilir:
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);
}
// ...
}
Bu demiyorlar çok önemli getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
önceden aynı istek üzerine vb. Aksi takdirde sunucu uygulaması kapsayıcısı istek gövdesini okuyacak ve ayrıştıracak ve böylece Apache Commons FileUpload boş bir istek gövdesi alacaktır. Ayrıca bkz. Ao ServletFileUpload # parseRequest (istek) boş bir liste döndürür .
Not edin FilenameUtils#getName()
. Bu dosya adını almak için bir MSIE düzeltmedir. Bu tarayıcı, yalnızca dosya adı yerine tam dosya yolunu ad boyunca yanlış gönderir.
Alternatif olarak, her şeyi otomatik olarak Filter
ayrıştıran bir öğeye sarılabilir request.getParameter()
ve her zamanki yolu kullanmaya devam edebilmeniz ve karşıya yüklenen dosyayı geri alabilmeniz için öğeleri isteğin parametre haritasına geri koyabilirsiniz request.getAttribute()
. Bu blog makalesinde bir örnek bulabilirsiniz .
GlassFish3 getParameter()
hala geri dönme hatası için geçici çözümnull
3.1.2'den eski Glassfish sürümlerinde hala geri dönen bir hata bulunduğunu unutmayın . Böyle bir kapsayıcıyı hedefliyorsanız ve yükseltemiyorsanız , bu yardımcı program yöntemiyle değeri ayıklamanız gerekir :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();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
Yüklenen dosya kaydetme (kullanmayın getRealPath()
ne de part.write()
!)
Elde edilen InputStream
( fileContent
yukarıdaki kod snippet'lerinde gösterildiği gibi değişken) diske veya veritabanına doğru bir şekilde kaydetme hakkında ayrıntılı bilgi için aşağıdaki cevaplara gidin :
Yüklenen dosyayı sunma
Kaydedilen dosyayı diskten veya veritabanından istemciye düzgün bir şekilde sunma konusunda ayrıntılı bilgi için aşağıdaki yanıtlara gidin:
Formu tahsis etme
Ajax (ve jQuery) kullanarak nasıl yükleme yapacağınız konusunda aşağıdaki yanıtlara gidin. Bunun için form verilerini toplamak üzere sunucu uygulaması kodunun değiştirilmesine gerek olmadığını unutmayın! Yalnızca yanıt verme şekliniz değiştirilebilir, ancak bu oldukça önemsizdir (yani, JSP'ye iletmek yerine, Ajax çağrısından sorumlu komut dosyasının beklediğine bağlı olarak bazı JSON veya XML veya düz metin yazdırmanız yeterlidir).
Umarım hepsi yardımcı olur :)