@@ -61,13 +61,17 @@ public function needsPartFile() {
6161	/** @var CappedMemoryCache|Result[] */ 
6262	private  $ objectCache
6363
64+ 	/** @var CappedMemoryCache|bool[] */ 
65+ 	private  $ directoryCache
66+ 
6467	/** @var CappedMemoryCache|array */ 
6568	private  $ filesCache
6669
6770	public  function  __construct ($ parameters
6871		parent ::__construct ($ parameters
6972		$ this parseParams ($ parameters
7073		$ this objectCache  = new  CappedMemoryCache ();
74+ 		$ this directoryCache  = new  CappedMemoryCache ();
7175		$ this filesCache  = new  CappedMemoryCache ();
7276	}
7377
@@ -98,6 +102,7 @@ private function cleanKey($path) {
98102
99103	private  function  clearCache () {
100104		$ this objectCache  = new  CappedMemoryCache ();
105+ 		$ this directoryCache  = new  CappedMemoryCache ();
101106		$ this filesCache  = new  CappedMemoryCache ();
102107	}
103108
@@ -110,7 +115,7 @@ private function invalidateCache($key) {
110115				unset($ this objectCache [$ existingKey
111116			}
112117		}
113- 		unset($ this filesCache [$ key
118+ 		unset($ this directoryCache [ $ key ],  $ this -> filesCache [$ key
114119	}
115120
116121	/** 
@@ -135,6 +140,41 @@ private function headObject($key) {
135140		return  $ this objectCache [$ key
136141	}
137142
143+ 	/** 
144+ 	 * Return true if directory exists 
145+ 	 * 
146+ 	 * There are no folders in s3. A folder like structure could be archived 
147+ 	 * by prefixing files with the folder name. 
148+ 	 * 
149+ 	 * Implementation from flysystem-aws-s3-v3: 
150+ 	 * https://github.com/thephpleague/flysystem-aws-s3-v3/blob/8241e9cc5b28f981e0d24cdaf9867f14c7498ae4/src/AwsS3Adapter.php#L670-L694 
151+ 	 * 
152+ 	 * @param $path 
153+ 	 * @return bool 
154+ 	 * @throws \Exception 
155+ 	 */ 
156+ 	private  function  doesDirectoryExist ($ path
157+ 		if  (!isset ($ this directoryCache [$ path
158+ 			// Maybe this isn't an actual key, but a prefix. 
159+ 			// Do a prefix listing of objects to determine. 
160+ 			try  {
161+ 				$ result$ this getConnection ()->listObjects ([
162+ 					'Bucket '  => $ this bucket ,
163+ 					'Prefix '  => rtrim ($ path'/ ' ) . '/ ' ,
164+ 					'MaxKeys '  => 1 ,
165+ 				]);
166+ 				$ this directoryCache [$ path$ result'Contents ' ] || $ result'CommonPrefixes ' ];
167+ 			} catch  (S3Exception $ e
168+ 				if  ($ egetStatusCode () === 403 ) {
169+ 					$ this directoryCache [$ pathfalse ;
170+ 				}
171+ 				throw  $ e
172+ 			}
173+ 		}
174+ 
175+ 		return  $ this directoryCache [$ path
176+ 	}
177+ 
138178	/** 
139179	 * Updates old storage ids (v0.2.1 and older) that are based on key and secret to new ones based on the bucket name. 
140180	 * TODO Do this in an update.php. requires iterating over all users and loading the mount.json from their home 
@@ -294,7 +334,9 @@ public function opendir($path) {
294334				// sub folders 
295335				if  (is_array ($ result'CommonPrefixes ' ])) {
296336					foreach  ($ result'CommonPrefixes ' ] as  $ prefix
297- 						$ filessubstr (trim ($ prefix'Prefix ' ], '/ ' ), strlen ($ path
337+ 						$ directoryNametrim ($ prefix'Prefix ' ], '/ ' );
338+ 						$ filessubstr ($ directoryNamestrlen ($ path
339+ 						$ this directoryCache [$ directoryNametrue ;
298340					}
299341				}
300342				if  (is_array ($ result'Contents ' ])) {
@@ -392,8 +434,13 @@ private function getLastModified($path) {
392434
393435	public  function  is_dir ($ path
394436		$ path$ this normalizePath ($ path
437+ 
438+ 		if  (isset ($ this filesCache [$ path
439+ 			return  false ;
440+ 		}
441+ 
395442		try  {
396- 			return  $ this isRoot ($ path$ this headObject ($ path .  ' / ' 
443+ 			return  $ this isRoot ($ path$ this doesDirectoryExist ($ path
397444		} catch  (S3Exception $ e
398445			\OC ::$ servergetLogger ()->logException ($ e'app '  => 'files_external ' ]);
399446			return  false ;
@@ -411,7 +458,7 @@ public function filetype($path) {
411458			if  (isset ($ this filesCache [$ path$ this headObject ($ path
412459				return  'file ' ;
413460			}
414- 			if  ($ this headObject ($ path .  ' / ' 
461+ 			if  ($ this doesDirectoryExist ($ path
415462				return  'dir ' ;
416463			}
417464		} catch  (S3Exception $ e
0 commit comments