/* * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.content; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This class is a simple utility to copy bytes from an {@link InputStream} to an {@link OutputStream}. * The copy can be performed with an optional byte limit and as soon as this limit is reached, * the copy will be stopped immediately and a {@link ContentLimitViolationException} will be thrown. * * @since Thor */ public final class LimitedStreamCopier { private static final Log logger = LogFactory.getLog(LimitedStreamCopier.class); private static final int BYTE_BUFFER_SIZE = 4096; /** * Copy of the the Spring FileCopyUtils, but does not silently absorb IOExceptions * when the streams are closed. We require the stream write to happen successfully. *

* Both streams are closed but any IOExceptions are thrown * * @param in the stream from which to read content. * @param out the stream to which to write content. * @param sizeLimit the maximum number of bytes that will be copied between the streams before a * {@link ContentLimitViolationException} will be thrown. * A negative number or zero will be deemed to mean 'no limit'. */ public final int copyStreams(InputStream in, OutputStream out, long sizeLimit) throws IOException { int byteCount = 0; IOException error = null; long totalBytesRead = 0; try { byte[] buffer = new byte[BYTE_BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = in.read(buffer)) != -1) { // We are able to abort the copy immediately upon limit violation. totalBytesRead += bytesRead; if (sizeLimit > 0 && totalBytesRead > sizeLimit) { StringBuilder msg = new StringBuilder(); msg.append("Content size violation, limit = ") .append(sizeLimit); throw new ContentLimitViolationException(msg.toString()); } out.write(buffer, 0, bytesRead); byteCount += bytesRead; } out.flush(); } finally { try { in.close(); } catch (IOException e) { error = e; logger.error("Failed to close output stream: " + this, e); } try { out.close(); } catch (IOException e) { error = e; logger.error("Failed to close output stream: " + this, e); } } if (error != null) { throw error; } return byteCount; } }