66import com .amazonaws .services .s3 .model .Bucket ;
77import com .amazonaws .services .s3 .model .ObjectMetadata ;
88import com .amazonaws .services .s3 .model .S3Object ;
9+ import com .amazonaws .services .s3 .model .S3ObjectId ;
910import com .google .common .base .Preconditions ;
1011import com .google .common .collect .ImmutableList ;
1112import com .google .common .collect .ImmutableSet ;
3132import java .util .concurrent .ConcurrentHashMap ;
3233import java .util .concurrent .ConcurrentMap ;
3334
35+ import org .slf4j .Logger ;
36+ import org .slf4j .LoggerFactory ;
37+
3438import static com .google .common .collect .Sets .difference ;
3539import static com .upplication .s3fs .AmazonS3Factory .*;
3640import static java .lang .String .format ;
41+ import static java .lang .String .format ;
3742
3843/**
3944 * Spec:
6570 */
6671public class S3FileSystemProvider extends FileSystemProvider {
6772
73+ private static final Logger LOGGER = LoggerFactory .getLogger (S3FileSystemProvider .class );
6874 public static final String CHARSET_KEY = "s3fs_charset" ;
6975 public static final String AMAZON_S3_FACTORY_CLASS = "s3fs_amazon_s3_factory" ;
76+ public static final String MULTIPART_UPLOAD_ENABLED = "s3fs_multipart_upload_enabled" ;
77+ public static final String MULTIPART_UPLOAD_PART_SIZE = "s3fs_multipart_upload_part_size" ;
78+ public static final String MULTIPART_UPLOAD_NUM_STREAMS = "s3fs_multipart_upload_num_streams" ;
79+ public static final String MULTIPART_UPLOAD_QUEUE_CAPACITY = "s3fs_multipart_upload_queue_capacity" ;
80+ public static final String MULTIPART_UPLOAD_NUM_UPLOAD_THREADS = "s3fs_multipart_upload_num_upload_threads" ;
7081
7182 private static final ConcurrentMap <String , S3FileSystem > fileSystems = new ConcurrentHashMap <>();
7283 private static final List <String > PROPS_TO_OVERLOAD = Arrays .asList (ACCESS_KEY , SECRET_KEY , REQUEST_METRIC_COLLECTOR_CLASS , CONNECTION_TIMEOUT , MAX_CONNECTIONS , MAX_ERROR_RETRY , PROTOCOL , PROXY_DOMAIN ,
7384 PROXY_HOST , PROXY_PASSWORD , PROXY_PORT , PROXY_USERNAME , PROXY_WORKSTATION , SOCKET_SEND_BUFFER_SIZE_HINT , SOCKET_RECEIVE_BUFFER_SIZE_HINT , SOCKET_TIMEOUT ,
74- USER_AGENT , AMAZON_S3_FACTORY_CLASS , SIGNER_OVERRIDE , PATH_STYLE_ACCESS );
85+ USER_AGENT , AMAZON_S3_FACTORY_CLASS , SIGNER_OVERRIDE , PATH_STYLE_ACCESS ,
86+ MULTIPART_UPLOAD_ENABLED , MULTIPART_UPLOAD_PART_SIZE , MULTIPART_UPLOAD_NUM_STREAMS ,
87+ MULTIPART_UPLOAD_QUEUE_CAPACITY , MULTIPART_UPLOAD_NUM_UPLOAD_THREADS );
7588
7689 private S3Utils s3Utils = new S3Utils ();
7790 private Cache cache = new Cache ();
@@ -95,6 +108,9 @@ public FileSystem newFileSystem(URI uri, Map<String, ?> env) {
95108 // create the filesystem with the final properties, store and return
96109 S3FileSystem fileSystem = createFileSystem (uri , props );
97110 fileSystems .put (fileSystem .getKey (), fileSystem );
111+
112+ LOGGER .debug ("New file system created. url:{}, props:{}" , uri , props );
113+
98114 return fileSystem ;
99115 }
100116
@@ -302,6 +318,8 @@ public Path getPath(URI uri) {
302318
303319 @ Override
304320 public DirectoryStream <Path > newDirectoryStream (Path dir , DirectoryStream .Filter <? super Path > filter ) throws IOException {
321+ LOGGER .debug ("New directory stream. path:{}, filter:{}" , dir , filter );
322+
305323 final S3Path s3Path = toS3Path (dir );
306324 return new DirectoryStream <Path >() {
307325 @ Override
@@ -316,8 +334,47 @@ public Iterator<Path> iterator() {
316334 };
317335 }
318336
337+ private S3MultipartUploadOutputStream createMultipartUploadOutputStream (final S3Path s3Path , Set <? extends OpenOption > opts ) throws IOException {
338+ final S3ObjectId objectId = s3Path .toS3ObjectId ();
339+ final Set <OpenOption > options = Sets .newHashSet (opts );
340+ final S3FileSystem fileSystem = s3Path .getFileSystem ();
341+ final Properties properties = fileSystem .getProperties ();
342+ final AmazonS3 client = s3Path .getFileSystem ().getClient ();
343+ final boolean createOpt = options .remove (StandardOpenOption .CREATE );
344+ final boolean createNewOpt = options .remove (StandardOpenOption .CREATE_NEW );
345+ final S3MultipartUploadOutputStream stream = new S3MultipartUploadOutputStream (client , objectId , properties );
346+
347+ // validate options
348+ if (options .isEmpty ()) {
349+ return stream ;
350+ }
351+
352+ // Remove irrelevant/ignored options
353+ options .remove (StandardOpenOption .WRITE );
354+ options .remove (StandardOpenOption .SPARSE );
355+ options .remove (StandardOpenOption .TRUNCATE_EXISTING );
356+
357+ if (!options .isEmpty ()) {
358+ throw new UnsupportedOperationException (format ("Unsupported operation: %s" , options ));
359+ }
360+
361+ if (createNewOpt && fileSystem .provider ().exists (s3Path )) {
362+ fileSystem .provider ().delete (s3Path );
363+ }
364+
365+ if (!createOpt && fileSystem .provider ().exists (s3Path )) {
366+ throw new FileAlreadyExistsException (format ("Target already exists: %s" , s3Path ));
367+ }
368+
369+ return stream ;
370+ }
371+
319372 @ Override
320373 public InputStream newInputStream (Path path , OpenOption ... options ) throws IOException {
374+ LOGGER .debug ("New input stream. path:{}, options:{}" , path , options );
375+
376+ System .out .println ("newInputStream" );
377+
321378 S3Path s3Path = toS3Path (path );
322379 String key = s3Path .getKey ();
323380
@@ -342,14 +399,46 @@ public InputStream newInputStream(Path path, OpenOption... options) throws IOExc
342399
343400 @ Override
344401 public SeekableByteChannel newByteChannel (Path path , Set <? extends OpenOption > options , FileAttribute <?>... attrs ) throws IOException {
345- S3Path s3Path = toS3Path (path );
346- return new S3SeekableByteChannel (s3Path , options );
402+ LOGGER .debug ("New byte channel. path:{}, options:{}" , path , options );
403+
404+ final S3Path s3Path = toS3Path (path );
405+ final boolean multipartEnabled = isMultipartUploadCapable (s3Path , options );
406+
407+ if (!multipartEnabled ) {
408+
409+ LOGGER .debug ("Using S3SeekableByteChannel" );
410+
411+ return new S3SeekableByteChannel (s3Path , options );
412+ }
413+
414+ LOGGER .debug ("Using S3MultipartFileChannel" );
415+
416+ final S3MultipartUploadOutputStream outputStream = createMultipartUploadOutputStream (s3Path , options );
417+ final FileChannel channel = new S3MultipartUploadChannel (outputStream );
418+
419+ return channel ;
347420 }
348421
349422 @ Override
350423 public FileChannel newFileChannel (Path path , Set <? extends OpenOption > options , FileAttribute <?>... attrs ) throws IOException {
351- S3Path s3Path = toS3Path (path );
352- return new S3FileChannel (s3Path , options );
424+ LOGGER .debug ("New file channel. path:{}, filter:{}" , path , options );
425+
426+ final S3Path s3Path = toS3Path (path );
427+ final boolean multipartEnabled = isMultipartUploadCapable (s3Path , options );
428+
429+ if (!multipartEnabled ) {
430+
431+ LOGGER .debug ("Using S3FileChannel" );
432+
433+ return new S3FileChannel (s3Path , options );
434+ }
435+
436+ LOGGER .debug ("Using S3MultipartFileChannel" );
437+
438+ final S3MultipartUploadOutputStream outputStream = createMultipartUploadOutputStream (s3Path , options );
439+ final FileChannel channel = new S3MultipartUploadChannel (outputStream );
440+
441+ return channel ;
353442 }
354443
355444 /**
@@ -359,6 +448,8 @@ public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options,
359448 */
360449 @ Override
361450 public void createDirectory (Path dir , FileAttribute <?>... attrs ) throws IOException {
451+ LOGGER .debug ("Create directory. path:{}, attrs:{}" , dir , attrs );
452+
362453 S3Path s3Path = toS3Path (dir );
363454 Preconditions .checkArgument (attrs .length == 0 , "attrs not yet supported: %s" , ImmutableList .copyOf (attrs )); // TODO
364455 if (exists (s3Path ))
@@ -378,6 +469,8 @@ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOExcept
378469
379470 @ Override
380471 public void delete (Path path ) throws IOException {
472+ LOGGER .debug ("Delete path:{}" , path );
473+
381474 S3Path s3Path = toS3Path (path );
382475 if (Files .notExists (s3Path ))
383476 throw new NoSuchFileException ("the path: " + this + " not exists" );
@@ -393,6 +486,8 @@ public void delete(Path path) throws IOException {
393486
394487 @ Override
395488 public void copy (Path source , Path target , CopyOption ... options ) throws IOException {
489+ LOGGER .debug ("Copy {} to target. options:{}" , source , target , options );
490+
396491 if (isSameFile (source , target ))
397492 return ;
398493
@@ -424,6 +519,8 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep
424519
425520 @ Override
426521 public void move (Path source , Path target , CopyOption ... options ) throws IOException {
522+ LOGGER .debug ("Move {} to target. options:{}" , source , target , options );
523+
427524 if (options != null && Arrays .asList (options ).contains (StandardCopyOption .ATOMIC_MOVE ))
428525 throw new AtomicMoveNotSupportedException (source .toString (), target .toString (), "Atomic not supported" );
429526 copy (source , target , options );
@@ -550,7 +647,7 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption..
550647 * @return S3FileSystem never null
551648 */
552649 public S3FileSystem createFileSystem (URI uri , Properties props ) {
553- return new S3FileSystem (this , getFileSystemKey (uri , props ), getAmazonS3 (uri , props ), uri .getHost ());
650+ return new S3FileSystem (this , getFileSystemKey (uri , props ), getAmazonS3 (uri , props ), uri .getHost (), props );
554651 }
555652
556653 protected AmazonS3 getAmazonS3 (URI uri , Properties props ) {
@@ -634,4 +731,16 @@ public Cache getCache() {
634731 public void setCache (Cache cache ) {
635732 this .cache = cache ;
636733 }
734+
735+ private boolean isMultipartUploadCapable (final S3Path s3Path , final Set <? extends OpenOption > options ) {
736+ // Not supported options
737+ if (options .contains (StandardOpenOption .READ ) || options .contains (StandardOpenOption .APPEND )) {
738+ return false ;
739+ }
740+
741+ final S3FileSystem fileSystem = s3Path .getFileSystem ();
742+ final Properties properties = fileSystem .getProperties ();
743+
744+ return Boolean .parseBoolean (properties .getProperty (MULTIPART_UPLOAD_ENABLED , "false" ));
745+ }
637746}
0 commit comments