diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index 9750173d1107f587f0329c6197398f9cdccd071b..f9fcfaf13c5ca1e4896688eea53020e7c3b10649 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -26,7 +26,7 @@ if (empty($_POST['dirToken'])) {
 
 	// return only read permissions for public upload
 	$allowedPermissions = OCP\PERMISSION_READ;
-	$public_directory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/';
+	$publicDirectory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/';
 
 	$linkItem = OCP\Share::getShareByToken($_POST['dirToken']);
 	if ($linkItem === false) {
@@ -50,13 +50,15 @@ if (empty($_POST['dirToken'])) {
 		$dir = sprintf(
 			"/%s/%s",
 			$path,
-			$public_directory
+			$publicDirectory
 		);
 
 		if (!$dir || empty($dir) || $dir === false) {
 			OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Unable to set upload directory.')))));
 			die();
 		}
+
+		$dir = rtrim($dir, '/');
 	}
 }
 
@@ -113,33 +115,33 @@ if ($maxUploadFileSize >= 0 and $totalSize > $maxUploadFileSize) {
 }
 
 $result = array();
-$directory = '';
 if (strpos($dir, '..') === false) {
 	$fileCount = count($files['name']);
 	for ($i = 0; $i < $fileCount; $i++) {
 
-		// Get the files directory
-		if(isset($_POST['file_directory']) === true) {
-			$directory = '/'.$_POST['file_directory'];
+		// target directory for when uploading folders
+		$relativePath = '';
+		if(!empty($_POST['file_directory'])) {
+			$relativePath = '/'.$_POST['file_directory'];
 		}
 
 		// $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder
 		if (isset($_POST['resolution']) && $_POST['resolution']==='autorename') {
 			// append a number in brackets like 'filename (2).ext'
-			$target = OCP\Files::buildNotExistingFileName(stripslashes($dir.$directory), $files['name'][$i]);
+			$target = OCP\Files::buildNotExistingFileName(stripslashes($dir . $relativePath), $files['name'][$i]);
 		} else {
-			$target = \OC\Files\Filesystem::normalizePath(stripslashes($dir.$directory).'/'.$files['name'][$i]);
+			$target = \OC\Files\Filesystem::normalizePath(stripslashes($dir . $relativePath).'/'.$files['name'][$i]);
 		}
-		
-		if(empty($directory) === true)
-		{
-			$directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir));
-			if (isset($public_directory)) {
-				// If we are uploading from the public app,
-				// we want to send the relative path in the ajax request.
-				$directory = $public_directory;
-			}
+
+		// relative dir to return to the client
+		if (isset($publicDirectory)) {
+			// path relative to the public root
+			$returnedDir = $publicDirectory . $relativePath;
+		} else {
+			// full path
+			$returnedDir = $dir . $relativePath;
 		}
+		$returnedDir = \OC\Files\Filesystem::normalizePath($returnedDir);
 
 		if ( ! \OC\Files\Filesystem::file_exists($target)
 			|| (isset($_POST['resolution']) && $_POST['resolution']==='replace')
@@ -163,7 +165,7 @@ if (strpos($dir, '..') === false) {
 						$data['uploadMaxFilesize'] = $maxUploadFileSize;
 						$data['maxHumanFilesize'] = $maxHumanFileSize;
 						$data['permissions'] = $meta['permissions'] & $allowedPermissions;
-						$data['directory'] = $directory;
+						$data['directory'] = $returnedDir;
 						$result[] = $data;
 					}
 
@@ -187,7 +189,7 @@ if (strpos($dir, '..') === false) {
 				$data['uploadMaxFilesize'] = $maxUploadFileSize;
 				$data['maxHumanFilesize'] = $maxHumanFileSize;
 				$data['permissions'] = $meta['permissions'] & $allowedPermissions;
-				$data['directory'] = $directory;
+				$data['directory'] = $returnedDir;
 				$result[] = $data;
 			}
 		}
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index 6b0ca793681089edd304deae6ae05d531546b15e..da58e1c31b8c1690496605d7ea15390566f643a0 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -346,7 +346,7 @@ OC.Upload = {
 						// noone set update parameters, we set the minimum
 						data.formData = {
 							requesttoken: oc_requesttoken,
-							dir: FileList.getCurrentDirectory(),
+							dir: data.targetDir || FileList.getCurrentDirectory(),
 							file_directory: fileDirectory
 						};
 					}
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 85c5d3f1bb0216b064c8f3669d6fc12f3787bda7..241997be2bcd2d54c0c0a421bf3c58082758db7c 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -1561,17 +1561,12 @@
 						dir = dropTarget.data('dir') || '/';
 					}
 
-					// update folder in form
-					data.formData = function() {
-						return [
-							{name: 'dir', value: dir},
-							{name: 'requesttoken', value: oc_requesttoken},
-							{name: 'file_directory', value: data.files[0].relativePath}
-						];
-					};
+					// add target dir
+					data.targetDir = dir;
 				} else {
 					// we are dropping somewhere inside the file list, which will
 					// upload the file to the current directory
+					data.targetDir = self.getCurrentDirectory();
 
 					// cancel uploads to current dir if no permission
 					var isCreatable = (self.getDirectoryPermissions() & OC.PERMISSION_CREATE) !== 0;
@@ -1654,15 +1649,34 @@
 						data.context.find('td.filesize').text(humanFileSize(size));
 					} else {
 						// only append new file if uploaded into the current folder
-						if (file.directory !== '/' && file.directory !== self.getCurrentDirectory()) {
+						if (file.directory !== self.getCurrentDirectory()) {
+							// Uploading folders actually uploads a list of files
+							// for which the target directory (file.directory) might lie deeper
+							// than the current directory
+
+							var fileDirectory = file.directory.replace('/','').replace(/\/$/, "");
+							var currentDirectory = self.getCurrentDirectory().replace('/','').replace(/\/$/, "") + '/';
+
+							if (currentDirectory !== '/') {
+								// abort if fileDirectory does not start with current one
+								if (fileDirectory.indexOf(currentDirectory) !== 0) {
+									return;
+								}
+
+								// remove the current directory part
+								fileDirectory = fileDirectory.substr(currentDirectory.length);
+							}
 
-							var fileDirectory = file.directory.replace('/','').replace(/\/$/, "").split('/');
+							// only take the first section of the path
+							fileDirectory = fileDirectory.split('/');
 
-							if (fileDirectory.length === 1) {
+							var fd;
+							// if the first section exists / is a subdir
+							if (fileDirectory.length) {
 								fileDirectory = fileDirectory[0];
 
-								// Get the directory
-								var fd = self.findFileEl(fileDirectory);
+								// See whether it is already in the list
+								fd = self.findFileEl(fileDirectory);
 								if (fd.length === 0) {
 									var dir = {
 										name: fileDirectory,
@@ -1672,19 +1686,15 @@
 										size: 0,
 										id: file.parentId
 									};
-									self.add(dir, {insert: true});
+									fd = self.add(dir, {insert: true});
 								}
-							} else {
-								fileDirectory = fileDirectory[0];
-							}
 
-							fileDirectory = self.findFileEl(fileDirectory);
-
-							// update folder size
-							size = parseInt(fileDirectory.attr('data-size'), 10);
-							size += parseInt(file.size, 10);
-							fileDirectory.attr('data-size', size);
-							fileDirectory.find('td.filesize').text(humanFileSize(size));
+								// update folder size
+								size = parseInt(fd.attr('data-size'), 10);
+								size += parseInt(file.size, 10);
+								fd.attr('data-size', size);
+								fd.find('td.filesize').text(OC.Util.humanFileSize(size));
+							}
 
 							return;
 						}
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 7d3bc946dd3a6793375a0988b4ea001b4c86361b..011e73d4b30994d6a3dbc2e557f6f7b0f5a68a80 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -1730,20 +1730,6 @@ describe('OCA.Files.FileList tests', function() {
 				return ev;
 			}
 
-			/**
-			 * Convert form data to a flat list
-			 * 
-			 * @param formData form data array as used by jquery.upload
-			 * @return map based on the array's key values
-			 */
-			function decodeFormData(data) {
-				var map = {};
-				_.each(data.formData(), function(entry) {
-					map[entry.name] = entry.value;
-				});
-				return map;
-			}
-
 			beforeEach(function() {
 				// simulate data structure from jquery.upload
 				uploadData = {
@@ -1803,11 +1789,7 @@ describe('OCA.Files.FileList tests', function() {
 				ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
 
 				expect(ev.result).not.toEqual(false);
-				expect(uploadData.formData).toBeDefined();
-				formData = decodeFormData(uploadData);
-				expect(formData.dir).toEqual('/subdir/somedir');
-				expect(formData.file_directory).toEqual('fileToUpload.txt');
-				expect(formData.requesttoken).toBeDefined();
+				expect(uploadData.targetDir).toEqual('/subdir/somedir');
 			});
 			it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
 				var ev, formData;
@@ -1815,11 +1797,7 @@ describe('OCA.Files.FileList tests', function() {
 				ev = dropOn(fileList.$el.find('.crumb:eq(2)'), uploadData);
 
 				expect(ev.result).not.toEqual(false);
-				expect(uploadData.formData).toBeDefined();
-				formData = decodeFormData(uploadData);
-				expect(formData.dir).toEqual('/a/b');
-				expect(formData.file_directory).toEqual('fileToUpload.txt');
-				expect(formData.requesttoken).toBeDefined();
+				expect(uploadData.targetDir).toEqual('/a/b');
 			});
 		});
 	});
diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js
index 80631908d24df715e6b145162d3c8c139e7f1fc9..1d633a655d5a2760f7d597a284940d312bf92ca8 100644
--- a/apps/files_sharing/js/public.js
+++ b/apps/files_sharing/js/public.js
@@ -128,7 +128,7 @@ OCA.Sharing.PublicApp = {
 				data.formData = {
 					requesttoken: $('#publicUploadRequestToken').val(),
 					dirToken: $('#dirToken').val(),
-					subdir: self.fileList.getCurrentDirectory(),
+					subdir: data.targetDir || self.fileList.getCurrentDirectory(),
 					file_directory: fileDirectory
 				};
 			});