UploadServlet.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.http.protocol.servlet;
import com.google.common.io.ByteSource;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.common.utility.WaarpSystemUtil;
import org.waarp.http.protocol.HttpHelper;
import org.waarp.http.protocol.HttpResumableInfo;
import org.waarp.http.protocol.HttpResumableSession;
import org.waarp.http.protocol.HttpSessions;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* Upload Servlet: enables uploading file from Web Browser from final user
*/
@MultipartConfig(fileSizeThreshold = 1024 * 1024)
public class UploadServlet extends AbstractServlet {
private static final long serialVersionUID = 2003L;
public static final String RESUMABLE_CHUNK_NUMBER = "resumableChunkNumber";
public static final String RESUMABLE_CHUNK_SIZE = "resumableChunkSize";
public static final String RESUMABLE_TOTAL_SIZE = "resumableTotalSize";
public static final String RESUMABLE_IDENTIFIER = "resumableIdentifier";
public static final String RESUMABLE_FILENAME = "resumableFilename";
public static final String RESUMABLE_RELATIVE_PATH = "resumableRelativePath";
public static final String FIELD_FILE = "file";
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(UploadServlet.class);
private static final MultipartConfigElement MULTI_PART_CONFIG =
new MultipartConfigElement("/tmp");
private static final String __MULTIPART_CONFIG_ELEMENT =
"org.eclipse.jetty.multipartConfig";
protected static final String SHA_256 = "sha256";
protected static final String INVALID_BLOCK = "Invalid block.";
@Override
protected final void doPost(final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException {
// Check that we have a file upload request
final boolean isMultipart =
request.getHeader("Content-Type").contains("multipart/");
InputStream inputStream = null;
final HttpResumableInfo resumableInfo;
final HttpResumableSession session;
final Map<String, String> arguments = new HashMap<String, String>();
logger.debug("MULTIPART MODE? {} {}", isMultipart,
request.getHeader("Content-Type"));
if (isMultipart) {
if (request.getAttribute(__MULTIPART_CONFIG_ELEMENT) == null) {
request.setAttribute(__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
}
try {
final Collection<Part> parts = request.getParts();
if (parts.isEmpty()) {
logger.warn("MULTIPART MODE BUT EMPTY");
inputStream = request.getInputStream();
} else {
for (final Part part : parts) {
if (part.getName().equalsIgnoreCase(FIELD_FILE)) {
inputStream = part.getInputStream();
} else {
final InputStream finalInputStream = part.getInputStream();
final ByteSource byteSource = new ByteSource() {
@Override
public final InputStream openStream() {
return finalInputStream;
}
};
final String valueText =
byteSource.asCharSource(WaarpStringUtils.UTF8).read();
arguments.put(part.getName(), valueText);
}
}
}
} catch (final ServletException e) {
logger.warn("MULTIPART MODE BUT error {}", e.getMessage());
try {
inputStream = request.getInputStream();
} catch (final IOException e2) {
throw new ServletException(INVALID_BLOCK + ": " + e2.getMessage());
}
} catch (final IOException e) {
logger.warn("MULTIPART MODE BUT error {}", e.getMessage());
try {
inputStream = request.getInputStream();
} catch (final IOException e2) {
throw new ServletException(INVALID_BLOCK + ": " + e2.getMessage());
}
}
} else {
final Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
final String name = names.nextElement();
arguments.put(name, request.getParameter(name));
}
try {
inputStream = request.getInputStream();
} catch (final IOException e) {
throw new ServletException(INVALID_BLOCK + ": " + e.getMessage());
}
}
logger.warn("PARAMS: {}", arguments);
resumableInfo = getResumableInfo(arguments);
logger.debug("RECV: {}", resumableInfo);
try {
session = getResumableSession(arguments, resumableInfo);//NOSONAR
} catch (final ServletException e) {
logger.error(e.getMessage());
response.setStatus(400);
return;
}
logger.debug("SESSION: {}", session);
try {
if (!session.tryWrite(resumableInfo, inputStream)) {
throw new ServletException(INVALID_BLOCK);
}
} catch (final IOException e) {
throw new ServletException(INVALID_BLOCK + ": " + e.getMessage());
}
String sha = arguments.get(SHA_256);
if (ParametersChecker.isNotEmpty(sha) &&
sha.equalsIgnoreCase("undefined")) {
sha = null;
}
try {
if (session.checkIfUploadFinished(sha)) {
// Check if all chunks uploaded, and change filename
logger.warn("ALL USER TRANSFER FINISHED: {}", session);
HttpSessions.getInstance().removeSession(session);
response.setStatus(200);
try {
response.getWriter().print("All finished.");
} catch (final IOException ignore) {
logger.debug(ignore);
}
} else {
logger.debug("PARTIAL UPLOAD: {}", session);
response.setStatus(201);
try {
response.getWriter().print("Upload");
} catch (final IOException ignore) {
logger.debug(ignore);
}
}
} catch (final IllegalArgumentException e) {
throw new ServletException(e);
}
}
/**
* From the HttpServletRequest, build the HttpResumableInfo
*
* @param arguments Map of arguments
*
* @return the HttpResumableInfo
*/
private HttpResumableInfo getResumableInfo(
final Map<String, String> arguments) {
final int resumableChunkNumber =
HttpHelper.toInt(arguments.get(RESUMABLE_CHUNK_NUMBER), -1);
final int resumableChunkSize =
HttpHelper.toInt(arguments.get(RESUMABLE_CHUNK_SIZE), -1);
final long resumableTotalSize =
HttpHelper.toLong(arguments.get(RESUMABLE_TOTAL_SIZE), -1);
final String resumableIdentifier = arguments.get(RESUMABLE_IDENTIFIER);
final String resumableFilename = arguments.get(RESUMABLE_FILENAME);
final String resumableRelativePath = arguments.get(RESUMABLE_RELATIVE_PATH);
return new HttpResumableInfo(resumableChunkNumber, resumableChunkSize,
resumableTotalSize, resumableIdentifier,
resumableFilename, resumableRelativePath);
}
/**
* From the HttpServletRequest and the HttpResumableInfo, build the
* HttpResumableSession
*
* @param arguments Map of arguments
* @param resumableInfo
*
* @return the HttpResumableSession
*
* @throws ServletException
*/
private HttpResumableSession getResumableSession(
final Map<String, String> arguments,
final HttpResumableInfo resumableInfo) throws ServletException {
final String rulename = arguments.get(RULENAME);
if (rulename == null) {
throw new ServletException(INVALID_REQUEST_PARAMS);
}
String comment = arguments.get(COMMENT);
if (comment == null) {
comment = "Web Upload " + resumableInfo.getIdentifier();
}
final HttpSessions sessions = HttpSessions.getInstance();
try {
final HttpAuthent authent =
(HttpAuthent) WaarpSystemUtil.newInstance(authentClass);
authent.initializeAuthent(arguments);
final HttpResumableSession session =
sessions.getOrCreateResumableSession(resumableInfo, rulename, comment,
authent);
if (!session.valid(resumableInfo)) {
sessions.removeSession(resumableInfo);
throw new ServletException(INVALID_REQUEST_PARAMS);
}
return session;
} catch (final IllegalArgumentException e) {
throw new ServletException(
INVALID_REQUEST_PARAMS + ": " + e.getMessage());
} catch (final InvocationTargetException e) {
throw new ServletException(
INVALID_REQUEST_PARAMS + ": " + e.getMessage());
}
}
@Override
protected final void doGet(final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException {
final Map<String, String> arguments = new HashMap<String, String>();
final Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
final String name = names.nextElement();
arguments.put(name, request.getParameter(name));
}
final HttpResumableInfo resumableInfo = getResumableInfo(arguments);
logger.debug("RECVGET: {}", resumableInfo);
final HttpResumableSession session;
try {
session = getResumableSession(arguments, resumableInfo);//NOSONAR
} catch (final ServletException e) {
logger.error(e.getMessage());
response.setStatus(400);
return;
}
logger.debug("SESSION: {}", session);
if (session.contains(resumableInfo)) {
logger.info("ALREADY: {}", session);
response.setStatus(200);
try {
response.getWriter().print("Uploaded."); //This Chunk has been Uploaded.
} catch (final IOException ignore) {
logger.debug(ignore);
}
} else {
logger.info("NOTDONE: {}", session);
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}