/*
* (c) Copyright Reserved EVRYTHNG Limited 2016. All rights reserved.
* Use of this material is subject to license.
* Copying and unauthorised use of this material strictly prohibited.
*/
package com.evrythng.java.wrapper.util;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
/**
* This class contains utility methods to upload files.
*/
public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
public static final String X_AMZ_ACL_HEADER_NAME = "x-amz-acl";
public static final String X_AMZ_ACL_HEADER_VALUE_PUBLIC_READ = "public-read";
public static final String X_AMZ_ACL_HEADER_VALUE_PRIVATE = "private";
/**
* Uploads file with public read access. Alias for {@link #uploadPublicFile(URL, File)}
*
* @param url upload url.
* @param contentType content type.
* @param file file for upload.
*/
public static void uploadFile(final URL url, final String contentType, final File file) throws IOException {
uploadPublicFile(url, contentType, file);
}
/**
* Uploads file with public read access.
*
* @param url upload url.
* @param contentType content type.
* @param file file for upload.
*/
public static void uploadPublicFile(final URL url, final String contentType, final File file) throws IOException {
HttpURLConnection connection = getConnectionForPublicUpload(url, contentType);
try (OutputStream output = connection.getOutputStream();
WritableByteChannel wbc = Channels.newChannel(output);
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel()) {
fc.transferTo(0, fc.size(), wbc);
}
validateConnectionAfterUpload(connection);
}
/**
* Uploads file with public read access. Will try to determine content type.
*
* @param url upload url.
* @param file file for upload.
*/
public static void uploadPublicFile(final URL url, final File file) throws IOException {
String contentType = URLConnection.guessContentTypeFromName(file.getName());
uploadPublicFile(url, contentType, file);
}
/**
* Uploads text as a file content with public read access. Alias for {@link #uploadPublicContent(URL, String, String)}
*
* @param url upload url.
* @param contentType content type.
* @param content text content.
*/
public static void uploadContent(final URL url, final String contentType, final String content) throws IOException {
uploadPublicContent(url, contentType, content);
}
/**
* Uploads text as a file content with public read access.
*
* @param url upload url.
* @param contentType content type.
* @param content text content.
*/
public static void uploadPublicContent(final URL url, final String contentType, final String content) throws IOException {
HttpURLConnection connection = getConnectionForPublicUpload(url, contentType);
try (OutputStream output = connection.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(output)) {
bos.write(content.getBytes());
}
validateConnectionAfterUpload(connection);
}
/**
* Uploads text as a file content with PRIVATE read access.
*
* @param uri upload {@link URI}
* @param contentTypeString content type
* @param contentString text content
* @throws IOException
*/
public static void uploadPrivateContent(final URI uri, final String contentTypeString, final String contentString) throws IOException {
LOGGER.info("uploadPrivateContent START: uri: [{}]; content type: [{}], content length: [{}]", new Object[] { uri, contentTypeString, contentString.length() });
CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
HttpPut httpPut = new HttpPut(uri);
httpPut.addHeader(HttpHeaders.CONTENT_TYPE, contentTypeString);
httpPut.addHeader(FileUtils.X_AMZ_ACL_HEADER_NAME, FileUtils.X_AMZ_ACL_HEADER_VALUE_PRIVATE);
ContentType contentType = ContentType.create(contentTypeString);
StringEntity stringEntity = new StringEntity(contentString, contentType);
httpPut.setEntity(stringEntity);
CloseableHttpResponse response = closeableHttpClient.execute(httpPut);
StatusLine statusLine = response.getStatusLine();
if (!(statusLine.getStatusCode() == HttpStatus.SC_OK)) {
throw new IOException(String.format("An error occurred while trying to upload private file - %d: %s", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
}
LOGGER.info("uploadPrivateContent END: uri: [{}]; content type: [{}], content length: [{}]", new Object[] { uri, contentTypeString, contentString.length() });
}
/**
* Uploads a {@code File} with PRIVATE read access.
*
* @param uri upload {@link URI}
* @param contentTypeString content type
* @param contentFile the file to upload
* @throws IOException
*/
public static void uploadPrivateContent(final URI uri, final String contentTypeString, final File contentFile) throws IOException {
LOGGER.info("uploadPrivateContent START: uri: [{}]; content type: [{}], content file: [{}]", new Object[] { uri, contentTypeString, contentFile });
CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
HttpPut httpPut = new HttpPut(uri);
httpPut.addHeader(HttpHeaders.CONTENT_TYPE, contentTypeString);
httpPut.addHeader(FileUtils.X_AMZ_ACL_HEADER_NAME, FileUtils.X_AMZ_ACL_HEADER_VALUE_PRIVATE);
ContentType contentType = ContentType.create(contentTypeString);
FileEntity fileEntity = new FileEntity(contentFile, contentType);
httpPut.setEntity(fileEntity);
CloseableHttpResponse response = closeableHttpClient.execute(httpPut);
StatusLine statusLine = response.getStatusLine();
if (!(statusLine.getStatusCode() == HttpStatus.SC_OK)) {
throw new IOException(String.format("An error occurred while trying to upload private file - %d: %s", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
}
LOGGER.info("uploadPrivateContent END: uri: [{}]; content type: [{}], content file: [{}]", new Object[] { uri, contentTypeString, contentFile });
}
/**
* Reads from {@link InputStream} provided and uploads data to Cloud as a file with public read access.
*
* Alias for {@link #uploadPublicStream(URL, String, InputStream)}
*
* @param url upload url.
* @param contentType content type.
* @param stream {@link InputStream} where to read from. Should be closed externally.
*/
public static void uploadStream(final URL url, final String contentType, final InputStream stream) throws IOException {
uploadPublicStream(url, contentType, stream);
}
/**
* Reads from {@link InputStream} provided and uploads data to Cloud as a file with public read access.
*
* @param url upload url.
* @param contentType content type.
* @param stream {@link InputStream} where to read from. Should be closed externally.
*/
public static void uploadPublicStream(final URL url, final String contentType, final InputStream stream) throws IOException {
HttpURLConnection connection = getConnectionForPublicUpload(url, contentType);
try (OutputStream output = connection.getOutputStream()) {
IOUtils.copy(stream, output);
}
validateConnectionAfterUpload(connection);
}
private static HttpURLConnection getConnectionForPublicUpload(final URL url, final String contentType) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpPut.METHOD_NAME);
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, contentType);
connection.setRequestProperty(X_AMZ_ACL_HEADER_NAME, X_AMZ_ACL_HEADER_VALUE_PUBLIC_READ);
connection.setDoOutput(true);
connection.connect();
return connection;
}
private static HttpURLConnection getConnectionForPrivateUpload(final URL url, final String contentType) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpPut.METHOD_NAME);
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, contentType);
connection.setRequestProperty(X_AMZ_ACL_HEADER_NAME, X_AMZ_ACL_HEADER_VALUE_PRIVATE);
connection.setDoOutput(true);
connection.connect();
return connection;
}
private static void validateConnectionAfterUpload(final HttpURLConnection connection) throws IOException {
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
try (final InputStream is = connection.getInputStream()) {
while (is.read() > 0) {
// consume
}
}
} else {
try (final InputStream is = connection.getErrorStream()) {
final String error = IOUtils.toString(is);
throw new IOException(
String.format("Unable to upload file. Got error %d %s: %s", responseCode, connection
.getResponseMessage(), error));
}
}
connection.disconnect();
}
}