/* * Copyright 2012-2014 Eric F. Savage, code@efsavage.com * * 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 com.ajah.amazon.s3; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import lombok.extern.java.Log; import org.jets3t.service.ServiceException; import org.jets3t.service.impl.rest.httpclient.RestS3Service; import org.jets3t.service.io.GZipDeflatingInputStream; import org.jets3t.service.model.S3Object; import org.jets3t.service.security.AWSCredentials; import com.ajah.lang.ConfigException; import com.ajah.util.AjahUtils; import com.ajah.util.config.Config; import com.ajah.util.lang.StreamUtils; import com.ajah.util.net.AjahMimeType; /** * Wrapper for {@link RestS3Service}. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, <a * href="mailto:code@efsavage.com">code@efsavage.com</a>. * */ @Log public class S3Client { /** * Returns the default client as configured by the aws.accessKey and * aws.secretKey properties. * * @return The default client. */ public static S3Client getDefaultClient() { try { // TODO Make this a singleton. return new S3Client(Config.i.get("aws.accessKey"), Config.i.get("aws.secretKey")); } catch (final S3Exception e) { throw new ConfigException(e); } } private RestS3Service s3Service; /** * Public constructor. Consider using {@link #getDefaultClient()}. * * @param accessKey * The AWS access key to use to authenticate. * @param secretKey * The AWS secret key to use to authenticate. * @throws S3Exception * If an S3 service could not be provisioned. */ public S3Client(final String accessKey, final String secretKey) throws S3Exception { final AWSCredentials awsCredentials = new AWSCredentials(accessKey, secretKey); try { this.s3Service = new RestS3Service(awsCredentials); } catch (final RuntimeException e) { throw new S3Exception(e); } } /** * Gets an object. * * @see S3Client#getDefaultClient() * * @param bucket * The bucket to put the object into, required. * @param name * The name to store the object as, required. * @param gzip * Un-Gzip the data? (if it has .gz to the end of it) * @return The file's data * @throws S3Exception * If an error occurs storing the object. */ public byte[] get(final Bucket bucket, final String name, final boolean gzip) throws S3Exception { try { log.finest("Fetching " + name + " from bucket " + bucket.getName()); final S3Object object = this.s3Service.getObject(bucket.toString(), name); if (gzip && name.endsWith(".gz")) { throw new UnsupportedOperationException(); } return StreamUtils.toByteArray(object.getDataInputStream()); } catch (final UnsupportedEncodingException e) { throw new ConfigException(e); } catch (IOException | ServiceException e) { throw new S3Exception(e); } } /** * Puts an object. Consider using {@link S3#put(Bucket, String, String)}. * * @see S3Client#getDefaultClient() * * @param bucket * The bucket to put the object into, required. * @param name * The name to store the object as, required. * @param data * The data of the object. * @param overwrite * Overwrite the file if it already exists? * @param gzip * Gzip the data (and add .gz to the end of it)? * @param mimeType * The mime type of the file, may be null. * @param acl * @throws S3Exception * If an error occurs storing the object. */ @SuppressWarnings("resource") public void put(final Bucket bucket, final String name, final byte[] data, final boolean overwrite, final boolean gzip, final AjahMimeType mimeType, final S3ACL acl) throws S3Exception { log.finest("Putting " + name + " in bucket " + bucket.getName()); if (data == null || data.length == 0) { log.warning("Data is empty, skipping upload"); return; } if (name.startsWith("/")) { log.warning("Name starts with \"/\", could cause unpredictable results"); } try (InputStream is = new ByteArrayInputStream(data)) { InputStream gzipIs = null; log.finest(data.length + " bytes to upload"); S3Object object; if (gzip) { object = new S3Object(name + ".gz"); gzipIs = new GZipDeflatingInputStream(is); object.setDataInputStream(gzipIs); } else { object = new S3Object(name); object.setDataInputStream(is); object.setContentLength(data.length); } if (mimeType != null) { object.setContentType(mimeType.getBaseType()); } object.setAcl(acl.getJets3t()); if (!overwrite && this.s3Service.isObjectInBucket(bucket.getName(), object.getName())) { log.fine(object.getName() + " already exists in bucket " + bucket.getName() + " and overwriting is disabled"); if (gzipIs != null) { gzipIs.close(); } return; } log.finest("Beginning upload of " + object.getName()); object = this.s3Service.putObject(bucket.toString(), object); log.fine("Uploaded " + object.getName() + " to bucket " + bucket.getName()); if (gzipIs != null) { gzipIs.close(); } } catch (final UnsupportedEncodingException e) { throw new ConfigException(e); } catch (IOException | ServiceException e) { throw new S3Exception(e); } } /** * Puts an object using the default client. Consider using * {@link S3#put(Bucket, String, String)}. * * @see S3Client#getDefaultClient() * * @param bucket * The bucket to put the object into, required. * @param name * The name to store the object as, required. * @param data * The data of the object. * @throws S3Exception * If an error occurs storing the object. */ public void put(final Bucket bucket, final String name, final String data) throws S3Exception { AjahUtils.requireParam(bucket, "bucket"); AjahUtils.requireParam(name, "name"); AjahUtils.requireParam(data, "data"); byte[] input; try { input = data.getBytes("UTF-8"); } catch (final UnsupportedEncodingException e) { throw new ConfigException(e); } put(bucket, name, input, true, true, AjahMimeType.TEXT_PLAIN, S3ACL.PRIVATE); } public void put(final Bucket bucket, final String name, final String data, final boolean overwrite, final boolean gzip, final AjahMimeType mimeType, final S3ACL acl) throws S3Exception { AjahUtils.requireParam(bucket, "bucket"); AjahUtils.requireParam(name, "name"); AjahUtils.requireParam(data, "data"); byte[] input; try { input = data.getBytes("UTF-8"); } catch (final UnsupportedEncodingException e) { throw new ConfigException(e); } put(bucket, name, input, overwrite, gzip, mimeType, acl); } }