package com.amazonaws.eclipse.explorer.s3.dnd; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.RandomAccessFile; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.part.IDropActionDelegate; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; public class S3ObjectSummaryDropAction implements IDropActionDelegate { public static final String ID = "com.amazonaws.eclipse.explorer.s3.objectSummaryDropAction"; public boolean run(Object source, Object target) { BucketAndKey bk = new BucketAndKey((byte[]) source); IContainer dropFolder; if ( target instanceof IContainer ) { dropFolder = (IContainer) target; } else if ( target instanceof IFile ) { dropFolder = ((IFile) target).getParent(); } else if ( target instanceof IJavaProject ) { dropFolder = ((IJavaProject) target).getProject(); } else if ( target instanceof IJavaElement ) { IJavaElement j = (IJavaElement) target; try { return run(source, j.getUnderlyingResource()); } catch ( JavaModelException e ) { AwsToolkitCore.getDefault().logException("Couldn't determine java resource", e); return false; } } else { return false; } final File f = dropFolder.getLocation().toFile(); if ( !f.exists() ) return false; String fileName = getOutputFileName(bk.key, f); if ( fileName == null || fileName.length() == 0 ) { return false; } final File outputFile = new File(fileName); new DownloadObjectJob("Downloading " + bk.key, bk.bucket, bk.key, dropFolder, outputFile).schedule(); return true; } /** * Opens a save-file dialog to prompt the user for a file name. */ private String getOutputFileName(final String key, final File f) { FileDialog dialog = new FileDialog(Display.getDefault().getActiveShell(), SWT.SAVE); dialog.setFilterPath(f.getAbsolutePath()); String keyToDisplay = key; if ( keyToDisplay.contains("/") ) { keyToDisplay = keyToDisplay.substring(keyToDisplay.lastIndexOf('/') + 1); } dialog.setFileName(keyToDisplay); dialog.setOverwrite(true); String fileName = dialog.open(); return fileName; } private final class DownloadObjectJob extends Job { private final String key; private final String bucket; private final IResource dropFolder; private final File outputFile; private DownloadObjectJob(String name, String bucket, String key, IContainer dropFolder, File outputFile) { super(name); this.bucket = bucket; this.key = key; this.dropFolder = dropFolder; this.outputFile = outputFile; } @Override protected IStatus run(final IProgressMonitor monitor) { FileOutputStream fos = null; try { // TODO: this won't work if the current account doesn't have read permission for the bucket and key AmazonS3 client = AwsToolkitCore.getClientFactory().getS3ClientForBucket(bucket); S3Object object = client.getObject(bucket, key); // This number is used for reporting only; the download // will appear to complete early if the file is bigger // than 2GB. long totalNumBytes = object.getObjectMetadata().getContentLength(); if ( totalNumBytes > Integer.MAX_VALUE ) totalNumBytes = Integer.MAX_VALUE; monitor.beginTask("Downloading", (int) totalNumBytes); // For a new file this is a no-op, but it truncates an // existing file for overwrite. RandomAccessFile raf = new RandomAccessFile(outputFile, "rw"); raf.setLength(0); raf.close(); fos = new FileOutputStream(outputFile); InputStream is = object.getObjectContent(); byte[] buffer = new byte[4096]; int bytesRead = 0; while ( (bytesRead = is.read(buffer)) > 0 ) { fos.write(buffer, 0, bytesRead); monitor.worked(bytesRead); } } catch ( Exception e ) { return new Status(Status.ERROR, AwsToolkitCore.PLUGIN_ID, "Error downloading file from S3", e); } finally { if ( fos != null ) { try { fos.close(); } catch ( Exception e ) { AwsToolkitCore.getDefault().logException("Couldn't close file output stream", e); } } monitor.done(); } // Refresh the drop folder // TODO: this won't work if they chose another folder in the // file selection dialog. Display.getDefault().asyncExec(new Runnable() { public void run() { try { dropFolder.refreshLocal(1, monitor); } catch ( CoreException e ) { AwsToolkitCore.getDefault().logException("Couldn't refresh local files", e); } } }); return Status.OK_STATUS; } } /** * Encodes the object summary as a byte array. */ public static byte[] encode(S3ObjectSummary s) { return BucketAndKey.encode(s); } static private class BucketAndKey { private String bucket; private String key; public static byte[] encode(S3ObjectSummary s) { StringBuilder b = new StringBuilder(); b.append(s.getBucketName()).append('\t').append(s.getKey()); return b.toString().getBytes(); } public BucketAndKey(byte[] data) { String s = new String(data); int index = s.indexOf('\t'); if ( index < 0 ) throw new RuntimeException("Unable to decode bucket and key"); bucket = s.substring(0, index); key = s.substring(index + 1); } } }