1515package com .microsoft .azure .storage .blob ;
1616
1717import com .microsoft .azure .storage .*;
18+ import com .microsoft .azure .storage .core .Base64 ;
1819import com .microsoft .azure .storage .core .Utility ;
1920import com .microsoft .azure .storage .file .CloudFile ;
2021import com .microsoft .azure .storage .file .CloudFileShare ;
3940import java .net .URI ;
4041import java .net .URISyntaxException ;
4142import java .security .InvalidKeyException ;
43+ import java .security .MessageDigest ;
44+ import java .security .NoSuchAlgorithmException ;
4245import java .util .*;
4346
4447import com .microsoft .azure .storage .TestRunners .CloudTests ;
@@ -605,6 +608,79 @@ public void testCopyBlockBlobSyncTest() throws InterruptedException, IOException
605608 this .doCloudBlockBlobCopy (false , false , true );
606609 }
607610
611+ @ Test
612+ public void testCopyBlockBlobWithSyncMd5 () throws URISyntaxException , StorageException , IOException , NoSuchAlgorithmException {
613+ // Create source on server. We must copy from a different account or the service will not validate the md5.
614+ CloudBlobContainer container = BlobTestHelper .getRandomCopySourceContainerReference ();
615+ container .create (BlobContainerPublicAccessType .CONTAINER , null , null );
616+ CloudBlockBlob source = container .getBlockBlobReference ("source" );
617+
618+ String data = "String data" ;
619+ source .uploadText (data , Constants .UTF8_CHARSET , null , null , null );
620+
621+ source .getMetadata ().put ("Test" , "value" );
622+ source .uploadMetadata ();
623+
624+ // Get destination reference
625+ CloudBlockBlob destination = this .container .getBlockBlobReference ("destination" );
626+ destination .commitBlockList (new ArrayList <BlockEntry >());
627+
628+ String copyId = null ;
629+ boolean exceptionThrown = false ;
630+ // Test that setting an md5 without sync copy is disallowed.
631+ try {
632+ destination .startCopy (source , "md5" , false , null , null , null , null );
633+ }
634+ catch (StorageException e ) {
635+ assertTrue (e .getCause () instanceof IllegalArgumentException );
636+ assertTrue (e .getMessage ().contains ("MD5" ));
637+ exceptionThrown = true ;
638+ }
639+ assertTrue (exceptionThrown );
640+
641+ // Test that the md5 header is set correctly by using an incorrect md5.
642+ exceptionThrown = false ;
643+ try {
644+ destination .startCopy (source , Base64 .encode (MessageDigest .getInstance ("MD5" ).digest ("garbage" .getBytes ())),
645+ true , null , null , null , null );
646+ }
647+ catch (StorageException e ) {
648+ exceptionThrown = true ;
649+ assertEquals ("Md5Mismatch" , e .getErrorCode ());
650+ String md5 = Base64 .encode (MessageDigest .getInstance ("MD5" ).digest (data .getBytes ()));
651+
652+ // Start a sync copy with a correct md5. Should succeed.
653+ copyId = destination .startCopy (source , md5 ,true , null , null , null , null );
654+ }
655+ assertTrue (exceptionThrown );
656+
657+ Calendar calendar = Calendar .getInstance (Utility .UTC_ZONE );
658+ destination .downloadAttributes ();
659+
660+ source .downloadAttributes ();
661+ assertNotNull (destination .getProperties ().getEtag ());
662+ assertFalse (source .getProperties ().getEtag ().equals (destination .getProperties ().getEtag ()));
663+ assertTrue (destination .getProperties ().getLastModified ().compareTo (new Date (calendar .get (Calendar .MINUTE ) - 1 )) > 0 );
664+
665+ String copyData = destination .downloadText (Constants .UTF8_CHARSET , null , null , null );
666+ assertEquals (data , copyData );
667+
668+ BlobProperties prop1 = destination .getProperties ();
669+ BlobProperties prop2 = source .getProperties ();
670+
671+ assertEquals (prop1 .getCacheControl (), prop2 .getCacheControl ());
672+ assertEquals (prop1 .getContentEncoding (), prop2 .getContentEncoding ());
673+ assertEquals (prop1 .getContentLanguage (), prop2 .getContentLanguage ());
674+ assertEquals (prop1 .getContentMD5 (), prop2 .getContentMD5 ());
675+ assertEquals (prop1 .getContentType (), prop2 .getContentType ());
676+
677+ assertEquals ("value" , destination .getMetadata ().get ("Test" ));
678+
679+ destination .delete ();
680+ source .delete ();
681+ container .deleteIfExists ();
682+ }
683+
608684 @ Test
609685 public void testCopyWithChineseChars () throws StorageException , IOException , URISyntaxException {
610686 String data = "sample data chinese chars 阿䶵" ;
@@ -637,8 +713,8 @@ public void eventOccurred(SendingRequestEvent eventArg) {
637713 }
638714 });
639715
640- copyDestination .startCopy (copySource .getUri (), false , null , null , null , ctx );
641- copyDestination .startCopy (copySource , false , null , null , null , ctx );
716+ copyDestination .startCopy (copySource .getUri (), null , false , null , null , null , ctx );
717+ copyDestination .startCopy (copySource , null , false , null , null , null , ctx );
642718 }
643719
644720 @ Test
@@ -1166,7 +1242,7 @@ public void testBlobDownloadRangeValidationTest() throws StorageException, URISy
11661242
11671243 @ Test
11681244 @ Category ({ DevFabricTests .class , DevStoreTests .class })
1169- public void testUploadBlockFromURI () throws URISyntaxException , StorageException , IOException {
1245+ public void testUploadBlockFromURI () throws URISyntaxException , StorageException , IOException , NoSuchAlgorithmException {
11701246 CloudBlobContainer container = BlobTestHelper .getRandomContainerReference ();
11711247 container .create (BlobContainerPublicAccessType .CONTAINER , null , null );
11721248 final CloudBlockBlob blob = container .getBlockBlobReference (BlobTestHelper
@@ -1181,14 +1257,24 @@ public void testUploadBlockFromURI() throws URISyntaxException, StorageException
11811257 final CloudBlockBlob blob2 = container .getBlockBlobReference (BlobTestHelper
11821258 .generateRandomBlobNameWithPrefix ("copyBlob" ));
11831259 Map <String , BlockEntry > blocks = BlobTestHelper .getBlockEntryList (2 );
1184- int i =0 ;
1185- for (BlockEntry block : blocks .values ()) {
1186- blob2 .uploadBlockFromURI (block .getId (), blob .getUri (), i *(text .length ()/blocks .values ().size ()),
1187- (long ) (text .length ()/blocks .values ().size ()));
1188- i ++;
1260+ // Copy the first half of the blob.
1261+ blob2 .uploadBlockFromURI (((BlockEntry )blocks .values ().toArray ()[0 ]).getId (), blob .getUri (), 0 , 5L );
1262+
1263+ // Copy the second half of the blob, specifying the MD5 and setting null for the length to indicate the remainder of the blob.
1264+ String md5 = Base64 .encode (MessageDigest .getInstance ("MD5" ).digest (text .substring (5 ).getBytes ()));
1265+ boolean exceptionThrown = false ;
1266+ try {
1267+ blob2 .uploadBlockFromURI (((BlockEntry ) blocks .values ().toArray ()[1 ]).getId (), blob .getUri (), 5 , null ,
1268+ Base64 .encode (MessageDigest .getInstance ("MD5" ).digest ("garbage" .getBytes ())), null , null , null );
11891269 }
1190- blob2 .commitBlockList (blocks .values ());
1270+ catch (StorageException e ) {
1271+ exceptionThrown = true ;
1272+ assertEquals ("Md5Mismatch" , e .getErrorCode ());
1273+ blob2 .uploadBlockFromURI (((BlockEntry ) blocks .values ().toArray ()[1 ]).getId (), blob .getUri (), 5 , null , md5 , null , null , null );
1274+ }
1275+ assertTrue (exceptionThrown );
11911276
1277+ blob2 .commitBlockList (blocks .values ());
11921278 assertEquals (blob2 .downloadText (), text );
11931279
11941280 container .deleteIfExists ();
@@ -2333,7 +2419,7 @@ private void doCloudBlockBlobCopy(boolean sourceIsSas, boolean destinationIsSas,
23332419 Thread .sleep (30000 );
23342420
23352421 // Start copy and wait for completion
2336- String copyId = copyDestination .startCopy (copySource , syncCopy , null , null , null , null );
2422+ String copyId = copyDestination .startCopy (copySource , null , syncCopy , null , null , null , null );
23372423 BlobTestHelper .waitForCopy (copyDestination );
23382424 Calendar calendar = Calendar .getInstance (Utility .UTC_ZONE );
23392425 destination .downloadAttributes ();
0 commit comments