diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
index e40df8eeb44a5fb311da1d059dedbd6e299243bb..3ae957a46b1861b8f056dfd6891298e50b58d5ec 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
@@ -61,6 +61,8 @@ public class StoreShareFileUploadServlet extends HttpServlet
 
     public static final String IGNORE_FILE_PATH_PARAM = "ignoreFilePath";
 
+    public static final String FOLDER_PATH_PARAM = "folderPath";
+
     public static final String UPLOAD_ID_PARAM = "uploadID";
 
     private PutDataSetService putService;
@@ -109,8 +111,12 @@ public class StoreShareFileUploadServlet extends HttpServlet
                      */
                     String filePath = uploadRequest.isIgnoreFilePath() ? FilenameUtils.getName(file.getName()) : file.getName();
 
-                    putService.putFileToStoreShare(uploadRequest.getSessionId(), filePath, uploadRequest.getDataSetType(),
-                            uploadRequest.getUploadId(), stream);
+                    operationLog.info("Received file '" + filePath + "' for upload id '" + uploadRequest.getUploadId() + "' and data set type '"
+                            + uploadRequest.getDataSetType() + "'");
+
+                    putService.putFileToStoreShare(uploadRequest.getSessionId(), uploadRequest.getFolderPath(), filePath,
+                            uploadRequest.getDataSetType(), uploadRequest.getUploadId(), stream);
+
                 } finally
                 {
                     IOUtils.closeQuietly(stream);
@@ -161,6 +167,11 @@ public class StoreShareFileUploadServlet extends HttpServlet
             }
         }
 
+        public String getFolderPath()
+        {
+            return request.getParameter(FOLDER_PATH_PARAM);
+        }
+
         public FileItemIterator getFiles() throws FileUploadException, IOException
         {
             ServletFileUpload upload = new ServletFileUpload();
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
index f1e8fe136846c1aa27fd28d3c9e10ec557ccb478..37f04bad595061aba16aba55a237726dfc832d14 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
@@ -296,7 +296,8 @@ public class PutDataSetService
         }
     }
 
-    public void putFileToStoreShare(String sessionToken, String filePath, String dataSetType, String uploadId, InputStream inputStream)
+    public void putFileToStoreShare(String sessionToken, String folderPathOrNull, String filePath, String dataSetType, String uploadId,
+            InputStream inputStream)
     {
         if (false == isInitialized)
         {
@@ -316,6 +317,10 @@ public class PutDataSetService
             {
                 throw new UserFailureException("File path cannot be null or empty");
             }
+            if (false == StringUtils.isBlank(folderPathOrNull) && folderPathOrNull.contains("../"))
+            {
+                throw new UserFailureException("Folder path must not contain '../'");
+            }
             if (filePath.contains("../"))
             {
                 throw new UserFailureException("File path must not contain '../'");
@@ -351,14 +356,22 @@ public class PutDataSetService
                 uploadSubDir.mkdir();
             }
 
-            File filePathDir = new File(uploadSubDir, FilenameUtils.getPath(filePath));
+            File filePathDir = null;
+            if (StringUtils.isBlank(folderPathOrNull))
+            {
+                filePathDir = new File(uploadSubDir, FilenameUtils.getPath(filePath));
+            } else
+            {
+                filePathDir = new File(uploadSubDir, FilenameUtils.getPath(folderPathOrNull + "/" + filePath));
+            }
             filePathDir.mkdirs();
 
             file = new File(filePathDir, FilenameUtils.getName(filePath));
             outputStream = new FileOutputStream(file);
-
             IOUtils.copyLarge(inputStream, outputStream);
 
+            operationLog.info("File '" + filePath + "' with upload id: '" + uploadId + "' has been stored as '" + file.getCanonicalPath() + "'");
+
         } catch (IOException ioe)
         {
             IOUtils.closeQuietly(outputStream);
diff --git a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
index 53f8ef2a2d9bc8f5479cfb95e161680c7994f55b..b928d5fb06dc67ab5059f8ae9f84ca3e2b628ff6 100644
--- a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
+++ b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
@@ -84,7 +84,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     public void testUploadWithInvalidSession() throws Exception
     {
         ContentResponse response =
-                uploadFiles("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", UUID.randomUUID().toString(), "UNKNOWN", true,
+                uploadFiles("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", UUID.randomUUID().toString(), "UNKNOWN", true, null,
                         new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("user is not logged in"));
@@ -93,7 +93,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     @Test
     public void testUploadWithoutSession() throws Exception
     {
-        ContentResponse response = uploadFiles(null, UUID.randomUUID().toString(), "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(null, UUID.randomUUID().toString(), "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Session token cannot be null or empty"));
     }
@@ -103,7 +103,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), null, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), null, true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Data set type cannot be null or empty"));
     }
@@ -113,7 +113,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, null, "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, null, "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id cannot be null or empty"));
     }
@@ -123,7 +123,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, "iam/incorrect", "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, "iam/incorrect", "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id must not contain '/'"));
     }
@@ -133,7 +133,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", true);
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", true, null);
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Please upload at least one file"));
     }
@@ -144,7 +144,8 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
         ContentResponse response =
-                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", "iam/../incorrect", "content"));
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, null,
+                        new FileToUpload("name", "iam/../incorrect", "content"));
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path must not contain '../'"));
     }
@@ -155,10 +156,21 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
         ContentResponse response =
-                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", null, "content"));
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, null, new FileToUpload("name", null, "content"));
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path cannot be null or empty"));
     }
+    
+    @Test
+    public void testUploadWithFolderPathWithFolderUp() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response =
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, "iam/../incorrect", new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Folder path must not contain '../'"));
+    }
 
     @Test
     public void testCreateWithInvalidSession() throws Exception
@@ -185,7 +197,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -212,7 +224,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -237,7 +249,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setTypeId(new EntityTypePermId(dataSetType));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -264,7 +276,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -291,7 +303,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -314,7 +326,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SamplePermId(samplePermId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -337,7 +349,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -365,7 +377,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -388,7 +400,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentPermId(experimentPermId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -415,7 +427,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setUploadId(uploadId);
         creation.setProperties(properties);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -441,7 +453,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setParentIds(Arrays.asList(parentId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -539,7 +551,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -574,7 +586,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2, file3, file4);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file1, file2, file3, file4);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -612,7 +624,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, null, file1, file2);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, null, null, file1, file2);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -647,7 +659,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file1, file2);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -666,6 +678,100 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         assertFile(files.get(5), "original/upload/test1.txt", file1.content);
     }
 
+    @Test
+    public void testCreateWithOneFolderPath() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "path/to/ignore/test1.txt", "test1 content");
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, "folderPath", file1);
+        assertResponseOK(response);
+
+        FileToUpload file2 = new FileToUpload("file2", "filePath/test2.txt", "test2 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPath", file2);
+        assertResponseOK(response);
+
+        FileToUpload file3 = new FileToUpload("file3", "/filePath/test3.txt", "test3 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPath/", file3);
+        assertResponseOK(response);
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(7, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/folderPath");
+        assertDirectory(files.get(3), "original/folderPath/filePath");
+        assertFile(files.get(4), "original/folderPath/filePath/test2.txt", file2.content);
+        assertFile(files.get(5), "original/folderPath/filePath/test3.txt", file3.content);
+        assertFile(files.get(6), "original/folderPath/test1.txt", file1.content);
+    }
+
+    @Test
+    public void testCreateWithMultipleFolderPaths() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "path/to/ignore/test1.txt", "test1 content");
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, "folderPathA", file1);
+        assertResponseOK(response);
+
+        FileToUpload file2 = new FileToUpload("file2", "filePath/test2.txt", "test2 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPathB", file2);
+        assertResponseOK(response);
+
+        FileToUpload file3 = new FileToUpload("file3", "/filePath/test3.txt", "test3 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPathB/", file3);
+        assertResponseOK(response);
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(9, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/upload");
+        assertDirectory(files.get(3), "original/upload/folderPathA");
+        assertFile(files.get(4), "original/upload/folderPathA/test1.txt", file1.content);
+        assertDirectory(files.get(5), "original/upload/folderPathB");
+        assertDirectory(files.get(6), "original/upload/folderPathB/filePath");
+        assertFile(files.get(7), "original/upload/folderPathB/filePath/test2.txt", file2.content);
+        assertFile(files.get(8), "original/upload/folderPathB/filePath/test3.txt", file3.content);
+    }
+
     @Test
     public void testCreateWithMultipleAttempts() throws Exception
     {
@@ -686,7 +792,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setProperties(properties);
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, file);
         assertResponseOK(response);
 
         // first attempt
@@ -728,7 +834,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, file);
         assertResponseOK(response);
 
         if (user.isDisabledProjectUser())
@@ -767,7 +873,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         }
     }
 
-    private ContentResponse uploadFiles(String sessionToken, String uploadId, String dataSetType, Boolean ignoreFilePath,
+    private ContentResponse uploadFiles(String sessionToken, String uploadId, String dataSetType, Boolean ignoreFilePath, String folderPath,
             FileToUpload... filesToUpload)
             throws InterruptedException, TimeoutException, ExecutionException
     {
@@ -795,6 +901,10 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         {
             request.param(StoreShareFileUploadServlet.IGNORE_FILE_PATH_PARAM, String.valueOf(ignoreFilePath));
         }
+        if (folderPath != null)
+        {
+            request.param(StoreShareFileUploadServlet.FOLDER_PATH_PARAM, String.valueOf(folderPath));
+        }
         if (dataSetType != null)
         {
             request.param(StoreShareFileUploadServlet.DATA_SET_TYPE_PARAM, dataSetType);
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
index 8ed2989b2e1144976e6cc5d38f585b2db9edd368..95925de5c24e5ff84e9c0996c1ed8f5a6d0edeb6 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
@@ -756,15 +756,15 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				// Selenium tests does not allow to use FormData class which
 				// would make constructing form data much easier
 
-				var formData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent1\r\n"
-						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file2.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent2\r\n"
+				var formData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"filePath/file1.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent1\r\n"
+						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"filePath/file2.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent2\r\n"
 						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW--";
 
 				var dataStoreFacade = facade.getDataStoreFacade("DSS1");
 
 				return dataStoreFacade.createDataSetUpload("UNKNOWN").then(function(upload) {
 					return $.ajax({
-						url : upload.url,
+						url : upload.getUrl("testFolder", false),
 						type : "POST",
 						processData : false,
 						contentType : "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
@@ -772,8 +772,8 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 					}).then(function() {
 						return c.createExperiment(facade).then(function(experimentPermId) {
 							var creation = new c.UploadedDataSetCreation();
-							creation.setUploadId(upload.id);
-							creation.setTypeId(new c.EntityTypePermId(upload.dataSetType));
+							creation.setUploadId(upload.getId());
+							creation.setTypeId(new c.EntityTypePermId(upload.getDataSetType()));
 							creation.setExperimentId(experimentPermId);
 							creation.setProperty("DESCRIPTION", "test description");
 							creation.setParentIds([ new c.DataSetPermId("20130424111751432-431") ]);
@@ -794,12 +794,13 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 
 					return dataStoreFacade.searchFiles(criteria, c.createDataSetFileFetchOptions()).then(function(result) {
 						var files = result.getObjects();
-						c.assertEqual(files.length, 5, "Number of files");
+						c.assertEqual(files.length, 6, "Number of files");
 						c.assertEqual(files[0].path, "", "Path 0");
 						c.assertEqual(files[1].path, "original", "Path 1");
-						c.assertEqual(files[2].path, "original/upload", "Path 2");
-						c.assertEqual(files[3].path, "original/upload/file1.txt", "Path 3");
-						c.assertEqual(files[4].path, "original/upload/file2.txt", "Path 4");
+						c.assertEqual(files[2].path, "original/testFolder", "Path 2");
+						c.assertEqual(files[3].path, "original/testFolder/filePath", "Path 3");
+						c.assertEqual(files[4].path, "original/testFolder/filePath/file1.txt", "Path 4");
+						c.assertEqual(files[5].path, "original/testFolder/filePath/file2.txt", "Path 5");
 
 						c.assertEqual(dataSet.getType().getCode(), "UNKNOWN", "Type code");
 						c.assertEqual(dataSet.getProperty("DESCRIPTION"), "test description", "'DESCRIPTION' property value");
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
index 7dcd63fadd7c87aaebd272d27c9cdceb0cb6d75d..3cc0cb188846c8fc08fe1a887f91eee22135e637 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
@@ -228,22 +228,35 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 						if (dataStores.length > 1) {
 							dfd.reject("Please specify exactly one data store");
 						} else {
+							var dataStore = dataStores[0];
 							var now = new Date();
 							var id = "upload-" + now.getFullYear() + pad(now.getMonth() + 1, 2) + pad(now.getDate(), 2) + pad(now.getHours(), 2) + pad(now.getMinutes(), 2) + pad(now.getSeconds(), 2)
 									+ "-" + pad(Math.round(Math.random() * 100000), 5);
 
-							var params = {
-								"sessionID" : facade._private.sessionToken,
-								"uploadID" : id,
-								"dataSetType" : dataSetType
-							};
-
-							var url = dataStores[0].downloadUrl + "/datastore_server/store_share_file_upload?" + jquery.param(params);
-
 							dfd.resolve({
-								"id" : id,
-								"url" : url,
-								"dataSetType" : dataSetType
+								"getId" : function() {
+									return id;
+								},
+								"getUrl" : function(folderPath, ignoreFilePath) {
+									var params = {
+										"sessionID" : facade._private.sessionToken,
+										"uploadID" : id,
+										"dataSetType" : dataSetType
+									};
+
+									if (folderPath != null) {
+										params["folderPath"] = folderPath;
+									}
+
+									if (ignoreFilePath != null) {
+										params["ignoreFilePath"] = ignoreFilePath;
+									}
+
+									return dataStore.downloadUrl + "/datastore_server/store_share_file_upload?" + jquery.param(params);
+								},
+								"getDataSetType" : function() {
+									return dataSetType;
+								}
 							});
 						}
 
@@ -569,7 +582,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createTags = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -765,7 +778,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updatePropertyTypes = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -798,7 +811,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateVocabularyTerms = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -809,7 +822,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateTags = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -999,7 +1012,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getVocabularyTerms = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1251,7 +1264,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchVocabularies = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1499,7 +1512,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deletePropertyTypes = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1521,7 +1534,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteVocabularyTerms = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1532,7 +1545,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteEntityTypes = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({