package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import static io.netty.buffer.Unpooled.*;
public abstract class AbstractDiskHttpData extends AbstractHttpData {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractDiskHttpData.class);
private File file;
private boolean isRenamed;
private FileChannel fileChannel;
protected AbstractDiskHttpData(String name, Charset charset, long size) {
super(name, charset, size);
}
protected abstract String getDiskFilename();
protected abstract String getPrefix();
protected abstract String getBaseDirectory();
protected abstract String getPostfix();
protected abstract boolean deleteOnExit();
private File tempFile() throws IOException {
String newpostfix;
String diskFilename = getDiskFilename();
if (diskFilename != null) {
newpostfix = '_' + diskFilename;
} else {
newpostfix = getPostfix();
}
File tmpFile;
if (getBaseDirectory() == null) {
tmpFile = File.createTempFile(getPrefix(), newpostfix);
} else {
tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
getBaseDirectory()));
}
if (deleteOnExit()) {
tmpFile.deleteOnExit();
}
return tmpFile;
}
@Override
public void setContent(ByteBuf buffer) throws IOException {
if (buffer == null) {
throw new NullPointerException("buffer");
}
try {
size = buffer.readableBytes();
checkSize(size);
if (definedSize > 0 && definedSize < size) {
throw new IOException("Out of size: " + size + " > " + definedSize);
}
if (file == null) {
file = tempFile();
}
if (buffer.readableBytes() == 0) {
if (!file.createNewFile()) {
if (file.length() == 0) {
return;
} else {
if (!file.delete() || !file.createNewFile()) {
throw new IOException("file exists already: " + file);
}
}
}
return;
}
FileOutputStream outputStream = new FileOutputStream(file);
try {
FileChannel localfileChannel = outputStream.getChannel();
ByteBuffer byteBuffer = buffer.nioBuffer();
int written = 0;
while (written < size) {
written += localfileChannel.write(byteBuffer);
}
buffer.readerIndex(buffer.readerIndex() + written);
localfileChannel.force(false);
} finally {
outputStream.close();
}
setCompleted();
} finally {
buffer.release();
}
}
@Override
public void addContent(ByteBuf buffer, boolean last)
throws IOException {
if (buffer != null) {
try {
int localsize = buffer.readableBytes();
checkSize(size + localsize);
if (definedSize > 0 && definedSize < size + localsize) {
throw new IOException("Out of size: " + (size + localsize) +
" > " + definedSize);
}
ByteBuffer byteBuffer = buffer.nioBufferCount() == 1 ? buffer.nioBuffer() : buffer.copy().nioBuffer();
int written = 0;
if (file == null) {
file = tempFile();
}
if (fileChannel == null) {
FileOutputStream outputStream = new FileOutputStream(file);
fileChannel = outputStream.getChannel();
}
while (written < localsize) {
written += fileChannel.write(byteBuffer);
}
size += localsize;
buffer.readerIndex(buffer.readerIndex() + written);
} finally {
buffer.release();
}
}
if (last) {
if (file == null) {
file = tempFile();
}
if (fileChannel == null) {
FileOutputStream outputStream = new FileOutputStream(file);
fileChannel = outputStream.getChannel();
}
fileChannel.force(false);
fileChannel.close();
fileChannel = null;
setCompleted();
} else {
if (buffer == null) {
throw new NullPointerException("buffer");
}
}
}
@Override
public void setContent(File file) throws IOException {
if (this.file != null) {
delete();
}
this.file = file;
size = file.length();
checkSize(size);
isRenamed = true;
setCompleted();
}
@Override
public void setContent(InputStream inputStream) throws IOException {
if (inputStream == null) {
throw new NullPointerException("inputStream");
}
if (file != null) {
delete();
}
file = tempFile();
FileOutputStream outputStream = new FileOutputStream(file);
int written = 0;
try {
FileChannel localfileChannel = outputStream.getChannel();
byte[] bytes = new byte[4096 * 4];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
int read = inputStream.read(bytes);
while (read > 0) {
byteBuffer.position(read).flip();
written += localfileChannel.write(byteBuffer);
checkSize(written);
read = inputStream.read(bytes);
}
localfileChannel.force(false);
} finally {
outputStream.close();
}
size = written;
if (definedSize > 0 && definedSize < size) {
if (!file.delete()) {
logger.warn("Failed to delete: {}", file);
}
file = null;
throw new IOException("Out of size: " + size + " > " + definedSize);
}
isRenamed = true;
setCompleted();
}
@Override
public void delete() {
if (fileChannel != null) {
try {
fileChannel.force(false);
fileChannel.close();
} catch (IOException e) {
logger.warn("Failed to close a file.", e);
}
fileChannel = null;
}
if (! isRenamed) {
if (file != null && file.exists()) {
if (!file.delete()) {
logger.warn("Failed to delete: {}", file);
}
}
file = null;
}
}
@Override
public byte[] get() throws IOException {
if (file == null) {
return EmptyArrays.EMPTY_BYTES;
}
return readFrom(file);
}
@Override
public ByteBuf getByteBuf() throws IOException {
if (file == null) {
return EMPTY_BUFFER;
}
byte[] array = readFrom(file);
return wrappedBuffer(array);
}
@Override
public ByteBuf getChunk(int length) throws IOException {
if (file == null || length == 0) {
return EMPTY_BUFFER;
}
if (fileChannel == null) {
FileInputStream inputStream = new FileInputStream(file);
fileChannel = inputStream.getChannel();
}
int read = 0;
ByteBuffer byteBuffer = ByteBuffer.allocate(length);
while (read < length) {
int readnow = fileChannel.read(byteBuffer);
if (readnow == -1) {
fileChannel.close();
fileChannel = null;
break;
} else {
read += readnow;
}
}
if (read == 0) {
return EMPTY_BUFFER;
}
byteBuffer.flip();
ByteBuf buffer = wrappedBuffer(byteBuffer);
buffer.readerIndex(0);
buffer.writerIndex(read);
return buffer;
}
@Override
public String getString() throws IOException {
return getString(HttpConstants.DEFAULT_CHARSET);
}
@Override
public String getString(Charset encoding) throws IOException {
if (file == null) {
return "";
}
if (encoding == null) {
byte[] array = readFrom(file);
return new String(array, HttpConstants.DEFAULT_CHARSET.name());
}
byte[] array = readFrom(file);
return new String(array, encoding.name());
}
@Override
public boolean isInMemory() {
return false;
}
@Override
public boolean renameTo(File dest) throws IOException {
if (dest == null) {
throw new NullPointerException("dest");
}
if (file == null) {
throw new IOException("No file defined so cannot be renamed");
}
if (!file.renameTo(dest)) {
IOException exception = null;
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
long chunkSize = 8196;
long position = 0;
try {
inputStream = new FileInputStream(file);
outputStream = new FileOutputStream(dest);
FileChannel in = inputStream.getChannel();
FileChannel out = outputStream.getChannel();
while (position < size) {
if (chunkSize < size - position) {
chunkSize = size - position;
}
position += in.transferTo(position, chunkSize , out);
}
} catch (IOException e) {
exception = e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
if (exception == null) {
exception = e;
} else {
logger.warn("Multiple exceptions detected, the following will be suppressed {}", e);
}
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
if (exception == null) {
exception = e;
} else {
logger.warn("Multiple exceptions detected, the following will be suppressed {}", e);
}
}
}
}
if (exception != null) {
throw exception;
}
if (position == size) {
if (!file.delete()) {
logger.warn("Failed to delete: {}", file);
}
file = dest;
isRenamed = true;
return true;
} else {
if (!dest.delete()) {
logger.warn("Failed to delete: {}", dest);
}
return false;
}
}
file = dest;
isRenamed = true;
return true;
}
private static byte[] readFrom(File src) throws IOException {
long srcsize = src.length();
if (srcsize > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"File too big to be loaded in memory");
}
FileInputStream inputStream = new FileInputStream(src);
byte[] array = new byte[(int) srcsize];
try {
FileChannel fileChannel = inputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
int read = 0;
while (read < srcsize) {
read += fileChannel.read(byteBuffer);
}
} finally {
inputStream.close();
}
return array;
}
@Override
public File getFile() throws IOException {
return file;
}
@Override
public HttpData touch() {
return this;
}
@Override
public HttpData touch(Object hint) {
return this;
}
}