/*
* Copyright (c) 2009, 2010, 2011, B3log Team
*
* 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 org.b3log.solo.action.file;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.b3log.latke.Keys;
import org.b3log.latke.action.util.PageCaches;
import org.b3log.latke.repository.Blob;
import org.b3log.latke.repository.Transaction;
import org.b3log.latke.util.Ids;
import org.b3log.latke.util.Locales;
import org.b3log.solo.model.ErrorPage;
import org.b3log.solo.model.File;
import org.b3log.solo.model.Preference;
import org.b3log.solo.repository.FileRepository;
import org.b3log.solo.repository.impl.FileGAERepository;
import org.b3log.solo.util.Preferences;
import org.b3log.solo.util.TimeZones;
import org.json.JSONObject;
/**
* File access via
* <a href="http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/package-summary.html">
* Google Data Store Low-level API</a>.
*
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.9, Feb 28, 2011
*/
public final class DataStoreFileAccessServlet extends HttpServlet {
/**
* Logger.
*/
private static final Logger LOGGER =
Logger.getLogger(DataStoreFileAccessServlet.class.getName());
/**
* Default serial version uid.
*/
private static final long serialVersionUID = 1L;
/**
* File repository.
*/
private FileRepository fileRepository =
FileGAERepository.getInstance();
/**
* Maximum entity size limited by data store.
*/
private static final long MAX_SIZE = 1024 * 1024;
/**
* Preference utilities.
*/
private Preferences preferenceUtils = Preferences.getInstance();
/**
* Time zone utilities.
*/
private TimeZones timeZoneUtils = TimeZones.getInstance();
@Override
protected void doPost(final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
final ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterator = null;
try {
iterator = upload.getItemIterator(request);
while (iterator.hasNext()) {
final FileItemStream item = iterator.next();
final InputStream stream = item.openStream();
final JSONObject preference = preferenceUtils.getPreference();
if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String localeString = preference.getString(
Preference.LOCALE_STRING);
final Locale locale = new Locale(
Locales.getLanguage(localeString),
Locales.getCountry(localeString));
if (!item.isFormField()) {
final ResourceBundle lang =
ResourceBundle.getBundle(Keys.LANGUAGE, locale);
// XXX: check size before streaming
final byte[] contentBytes = IOUtils.toByteArray(stream);
if (contentBytes.length > MAX_SIZE) {
final String fail = lang.getString("uploadFailLabel");
final String cause =
lang.getString("exceedMaxUploadSizeLabel");
sendError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
fail, cause);
return;
}
if (0 == contentBytes.length) {
final String fail = lang.getString("uploadFailLabel");
final String cause = lang.getString("fileEmptyLabel");
sendError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
fail, cause);
return;
}
final Blob blob = new Blob(contentBytes);
final JSONObject file = new JSONObject();
final String id = Ids.genTimeMillisId();
file.put(Keys.OBJECT_ID, id);
file.put(File.FILE_CONTENT_TYPE, item.getContentType());
file.put(File.FILE_CONTENT, blob);
file.put(File.FILE_DOWNLOAD_COUNT, 0);
final String timeZoneId =
preference.getString(Preference.TIME_ZONE_ID);
final Date createDate = timeZoneUtils.getTime(timeZoneId);
file.put(File.FILE_UPLOAD_DATE, createDate);
final String fileName = item.getName();
file.put(File.FILE_NAME, fileName);
final long fileSize = contentBytes.length;
file.put(File.FILE_SIZE, fileSize);
final String downloadURL = "/datastore-file-access.do?oId="
+ id;
file.put(File.FILE_DOWNLOAD_URL, downloadURL);
fileRepository.add(file);
PageCaches.removeAll(); // XXX: use repository cache instead
}
}
response.sendRedirect("/admin-index.do");
} catch (final Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new ServletException("File upload error: " + e.getMessage());
}
}
@Override
protected void doGet(final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
final String id = request.getParameter(Keys.OBJECT_ID);
final Transaction transaction = fileRepository.beginTransaction();
try {
final JSONObject file = fileRepository.get(id);
if (null == file) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final Blob content = (Blob) file.get(File.FILE_CONTENT);
final String name = file.getString(File.FILE_NAME);
response.addHeader("Content-Disposition",
"attachment; filename="
+ URLEncoder.encode(name, "UTF-8"));
response.setContentType(file.getString(File.FILE_CONTENT_TYPE));
response.getOutputStream().write(content.getBytes());
response.getOutputStream().close();
final int downloadCnt = file.getInt(File.FILE_DOWNLOAD_COUNT);
file.put(File.FILE_DOWNLOAD_COUNT, downloadCnt + 1);
fileRepository.update(id, file);
transaction.commit();
} catch (final Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new ServletException("File download error: " + e.getMessage());
}
}
/**
* Sends error via {@linkplain HttpServletResponse#sendError(int, java.lang.String)}
* with the specified error URI and cause.
*
* @param request the specified http servlet request
* @param response the specified http servlet response
* @param errorCode the specified error code
* @param errorURI the specified error URI
* @param cause the specified cause
* @throws IOException io exception
*/
private void sendError(final HttpServletRequest request,
final HttpServletResponse response,
final int errorCode,
final String errorURI,
final String cause) throws IOException {
request.setAttribute(ErrorPage.ERROR_PAGE_REQUEST_URI,
request.getRequestURI());
request.setAttribute(ErrorPage.ERROR_PAGE_CAUSE, errorURI + cause);
response.sendError(errorCode);
}
}