All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertigo.struts2.core.VFileResponseBuilder Maven / Gradle / Ivy

The newest version!
/**
 * vertigo - simple java starter
 *
 * Copyright (C) 2013, KleeGroup, [email protected] (http://www.kleegroup.com)
 * KleeGroup, Centre d'affaire la Boursidiere - BP 159 - 92357 Le Plessis Robinson Cedex - France
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.vertigo.struts2.core;

import io.vertigo.dynamo.file.model.VFile;
import io.vertigo.lang.Assertion;
import io.vertigo.lang.WrappedException;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Builder d'envoi de Fichier.
 *
 * @author npiedeloup
 */
public final class VFileResponseBuilder {
	private static final String NOT_ALLOWED_IN_FILENAME = "\\/:*?\"<>|;";

	private final HttpServletRequest httpRequest;
	private final HttpServletResponse httpResponse;

	/**
	 * Constructeur.
	 * @param httpRequest ServletRequest
	 * @param httpResponse ServletResponse
	 */
	public VFileResponseBuilder(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {
		Assertion.checkNotNull(httpRequest);
		Assertion.checkNotNull(httpResponse);
		//-----
		this.httpRequest = httpRequest;
		this.httpResponse = httpResponse;
	}

	/**
	 * Envoi les données au client sous forme d'attachment.
	 * @param vFile Fichier a envoyer
	 */
	public void send(final VFile vFile) {
		send(vFile, true);
	}

	/**
	 * Envoi les données au client sous forme de stream.
	 * @param vFile Fichier a envoyer
	 */
	public void sendAsStream(final VFile vFile) {
		send(vFile, false);
	}

	private void send(final VFile vFile, final boolean attachment) {
		try {
			doSend(vFile, attachment);
		} catch (final IOException e) {
			handleException(e);
		}
	}

	private void doSend(final VFile vFile, final boolean attachment) throws IOException {
		final Long length = vFile.getLength();
		Assertion.checkArgument(length.longValue() < Integer.MAX_VALUE, "Le fichier est trop gros pour être envoyé. Il fait " + length.longValue() / 1024 + " Ko, mais le maximum acceptable est de " + (Integer.MAX_VALUE / 1024) + " Ko.");
		httpResponse.setContentLength(length.intValue());
		httpResponse.addHeader("Content-Disposition", encodeFileNameToContentDisposition(httpRequest, vFile.getFileName(), attachment));
		httpResponse.setDateHeader("Last-Modified", vFile.getLastModified().getTime());
		httpResponse.setContentType(vFile.getMimeType());

		try (final InputStream input = vFile.createInputStream()) {
			try (final BufferedInputStream bInput = new BufferedInputStream(input)) {
				try (final OutputStream output = httpResponse.getOutputStream()) {
					copy(bInput, output);
				}
			}
		}
	}

	/**
	 * Gestion des exceptions d'export.
	 * @param exception L'exception à gérer
	 */
	private static void handleException(final Exception exception) {
		throw new WrappedException("Impossible d'envoyer le fichier.", exception);
	}

	/**
	 * Encode fileName according to RFC 5987.
	 * @param request HttpServletRequest
	 * @param fileName String
	 * @param isAttachment boolean is Content an attachment
	 * @return String
	 */
	private static String encodeFileNameToContentDisposition(final HttpServletRequest request, final String fileName,
			final boolean isAttachment) {
		if (fileName == null) {
			return "";
		}
		// on remplace par des espaces les caractères interdits dans les noms de fichiers : \ / : * ? " < > | ;
		final int notAllowedLength = NOT_ALLOWED_IN_FILENAME.length();
		String cleanFileName = fileName; //only accepted char
		for (int i = 0; i < notAllowedLength; i++) {
			cleanFileName = cleanFileName.replace(NOT_ALLOWED_IN_FILENAME.charAt(i), '_');
		}

		final int length = cleanFileName.length();
		final StringBuilder sb = new StringBuilder(length + length / 4);
		if (isAttachment) {
			sb.append("attachment;");
		}
		final String cleanestFileName = cleanFileName.replaceAll(" ", "%20"); //cleanest for default fileName
		sb.append("filename=").append(cleanestFileName);
		byte[] utf8FileName;
		try {
			utf8FileName = cleanFileName.getBytes("utf8"); //Utf8 fileName
			sb.append(";filename*=UTF-8''");
			for (final byte c : utf8FileName) {
				if (isSimpleLetterOrDigit(c) || c == '.' || c == '-' || c == '_') {
					sb.append((char) c);
				} else {
					sb.append("%");
					sb.append(Integer.toHexString(c & 0xff)); // we want byte as a char on one byte
				}
			}
		} catch (final UnsupportedEncodingException e) {
			throw new AssertionError(e); // can't ever happen
		}
		return sb.toString();
	}

	/**
	 * Copie le contenu d'un flux d'entrée vers un flux de sortie.
	 * @param in flux d'entrée
	 * @param out flux de sortie
	 * @throws IOException Erreur d'entrée/sortie
	 */
	private static void copy(final InputStream in, final OutputStream out) throws IOException {
		final int bufferSize = 10 * 1024;
		final byte[] bytes = new byte[bufferSize];
		int read = in.read(bytes);
		while (read != -1) {
			out.write(bytes, 0, read);
			read = in.read(bytes);
		}
	}

	private static boolean isSimpleLetterOrDigit(final byte c) {
		return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9';
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy