diff --git a/CODING.md b/CODING.md
index c14b9849e914c2a206d1c8d3d983155e799ae79b..9c969543347893005f867a23215083b3ba5b020c 100644
--- a/CODING.md
+++ b/CODING.md
@@ -158,8 +158,8 @@ New Button
       * Client stays on current page.
   
 
-File Handling
--------------
+File Handling: Upload
+---------------------
 * No previous uploaded file present
   1. User presses the Browse button
     1. User selects file
@@ -173,6 +173,39 @@ File Handling
     1. File delete button gets disabled and hidden
     1. Browse button gets enabled and displayed
   
+Form Build (load)
+.................
+* The upload functionality consist of three elements
+  * 1) A <div> tag with a) an optional filename of an earlier uploaded file plus and b) a trash Button.
+  * 2) The 'browse' button (<input type='file' name='_upload_<feName>'>). This element will not be send by post.
+  * 3) A HTML hidden element with name=<feName> containing the <sipUpload>.
+* A new uniq SIP (sipUpload) will be created for every upload formElement. These 'sipUpload' will be assigned to the browse button and to the delete button.  
+  * The individual sipUpload is necessary to correctly handle multiple simustaenously forms when using r=0. Also, through this uniq id it's easy to distinguish between asynchron uploaded files.
+  * The SIP contains the '_FILES' information submitted during the upload.
+* Via the hidden element <feName> 'save()' access the form individual upload status informations. 
+
+Upload to server, before 'save'
+...............................
+* If a user open's a file for upload via the browse button, that file is immediately transmitted to the server. The user will see a turning wheel during the upload time.
+* On success the 'Browse; Button disappears and the filename plus the delete button will be displayed (client logic). 
+* The uploaded file will be checked: maxsize, mime type, check script.
+* The uploaded file is still temporary. It has been renamed from $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] to  $_SESSION['X'][<uploadSip>][FILES_TMP_NAME].cached
+* The upload action will be saved in the user session.
+  *  $_SESSION['X'][<uploadSip>][FILES_TMP_NAME|FILES_NAME|FILES_ERROR|FILE_SIZE]
+* Clicks the user on delete button.
+  * In the usersession a flagDelete will be set: $_SESSION['X'][<uploadSip>]['flagDelete']='1'
+  * An optional previous upload file (still not saved on the final place) will be deleted.
+  * An optional existing variable $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] will be deleted. The 'flagDelete' must not be change - it's later needed to detect to delete earlier uploaded files.
+
+Form save
+.........
+* Before building the insert/update, process all 'uploads'.
+* Get every uniq sipUpload to every upload formElement. Get the corresponding temporary uploaded filename.
+* If $_SESSION['X'][<uploadSip>]['flagDelete']='1' is set, delete prefious uploaded file.
+* Calculate <destination>
+* mv <file>.cached <destination>
+* clientvalue[<feName>] = <destination>
+* delete $_SESSION['X'][<uploadSip>]
 
 Formelement type: DATE / DATETIME / TIME
 ----------------------------------------
diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index 1fdadd1e9f213f5b117c57e2870dd5f1b8ca07f9..757173ae2109ac2cbdb4537879c76c9cfca97ed7 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -42,8 +42,16 @@ abstract class AbstractBuildForm {
 
 //    protected $feDivClass = array(); // Wrap FormElements in <div class="$feDivClass[type]">
 
+    /**
+     * @var string
+     */
     private $formId = null;
 
+    /**
+     * @var Sip
+     */
+    private $sip = null;
+
     /**
      * AbstractBuildForm constructor.
      *
@@ -60,7 +68,7 @@ abstract class AbstractBuildForm {
         $this->evaluate = new Evaluate($this->store, $this->db);
         $this->showDebugInfo = ($this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) === 'yes');
 
-//        $sip = $this->store->getVar(CLIENT_SIP, STORE_CLIENT);
+        $this->sip = $this->store->getSipInstance();
 
         // render mode specific
         $this->fillWrap();
@@ -1505,8 +1513,16 @@ abstract class AbstractBuildForm {
     public function buildFile(array $formElement, $htmlFormElementId, $value, &$json) {
         $attribute = '';
 
-        $sip = $this->store->getVar(SIP_SIP, STORE_SIP);
-        $value = basename($value); // Strip directories
+        $arr = array();
+        $arr['fake_uniq_never_use_this'] = uniqid(); // make sure we get a new SIP. This is needed for multiple forms (same user) with r=0
+        $arr[CLIENT_SIP_FOR_FORM] = $this->store->getVar(SIP_SIP, STORE_SIP);
+        $arr[CLIENT_FE_NAME] = $formElement['name'];
+        $arr[CLIENT_FORM] = $this->formSpec['name'];
+        $arr[CLIENT_RECORD_ID] = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
+        $arr[CLIENT_PAGE_ID] = 'fake';
+        $sipUpload = $this->sip->queryStringToSip(OnArray::toString($arr), RETURN_SIP);
+
+        $hiddenSipUpload = $this->buildNativeHidden($htmlFormElementId, $sipUpload);
 
         $attribute .= Support::doAttribute('name', $htmlFormElementId);
 //        $attribute .= Support::doAttribute('class', 'form-control');
@@ -1514,6 +1530,7 @@ abstract class AbstractBuildForm {
         $attribute .= Support::doAttribute('title', $formElement['tooltip']);
         $attribute .= $this->getAttributeList($formElement, ['autofocus', 'accept']);
         $attribute .= Support::doAttribute('data-load', ($formElement['dynamicUpdate'] === 'yes') ? 'data-load' : '');
+        $attribute .= Support::doAttribute('data-sip', $sipUpload);
 
         if ($value === '') {
             $textDeleteClass = 'hidden';
@@ -1528,7 +1545,7 @@ abstract class AbstractBuildForm {
         $attribute .= Support::doAttribute('class', $uploadClass, true);
         $htmlInputFile = '<input ' . $attribute . '>' . $this->getHelpBlock();
 
-        $deleteButton = Support::wrapTag("<button class='delete-file' data-sip='$sip' name='delete-$htmlFormElementId'>", $this->symbol[SYMBOL_DELETE]);
+        $deleteButton = Support::wrapTag("<button class='delete-file' data-sip='$sipUpload' name='delete-$htmlFormElementId'>", $this->symbol[SYMBOL_DELETE]);
 
         $htmlFilename = Support::wrapTag("<span class='uploaded-file-name'>", $value, false);
         $htmlTextDelete = Support::wrapTag("<div class='uploaded-file $textDeleteClass'>", $htmlFilename . ' ' . $deleteButton);
@@ -1537,7 +1554,7 @@ abstract class AbstractBuildForm {
 
         $json = $this->getJsonElementUpdate($htmlFormElementId, $value, $formElement['mode']);
 
-        return $htmlTextDelete . $htmlInputFile;
+        return $htmlTextDelete . $htmlInputFile . $hiddenSipUpload;
     }
 
     /**
diff --git a/extension/qfq/qfq/BuildFormBootstrap.php b/extension/qfq/qfq/BuildFormBootstrap.php
index db8465282e3d7866ed3099b98e6b79e966f0ccbf..3ab2562bd254f880b87968df82782dd7e295c1ee 100644
--- a/extension/qfq/qfq/BuildFormBootstrap.php
+++ b/extension/qfq/qfq/BuildFormBootstrap.php
@@ -311,6 +311,9 @@ class BuildFormBootstrap extends AbstractBuildForm {
             $deleteUrl = $this->createDeleteUrl($this->formSpec['tableName'], $recordId);
         }
 
+        $actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
+        $actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;
+
         $html .= '</form>';  //  <form class="form-horizontal" ...
         $html .= <<<EOF
         <script type="text/javascript">
@@ -324,7 +327,8 @@ class BuildFormBootstrap extends AbstractBuildForm {
                     submitTo: 'typo3conf/ext/qfq/qfq/api/save.php',
                     deleteUrl: '$deleteUrl',
                     refreshUrl: "typo3conf/ext/qfq/qfq/api/load.php",
-                    fileUploadTo: 'typo3conf/ext/qfq/qfq/api/file.php'
+                    fileUploadTo: 'typo3conf/ext/qfq/qfq/api/file.php?$actionUpload',
+                    fileDeleteUrl: 'typo3conf/ext/qfq/qfq/api/file.php?$actionDelete'
                 });
 
                 var qfqRecordList = new QfqNS.QfqRecordList('typo3conf/ext/qfq/qfq/api/delete.php');
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index a483238b60bfa19da88c4c4bb9d14c475ac24f68..dc8d4b1f9120c44eb189a32b1b6c14ca348f4296 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -143,6 +143,9 @@ const ERROR_STORE_KEY_EXIST = 1101;
 const ERROR_IO_READ_FILE = 1200;
 const ERROR_IO_WRITE = 1203;
 const ERROR_IO_OPEN = 1204;
+const ERROR_IO_UNLINK = 1205;
+const ERROR_IO_FILE_EXIST = 1206;
+const ERROR_IO_RENAME = 1207;
 
 //Report
 const ERROR_UNKNOWN_LINK_QUALIFIER = 1300;
@@ -153,7 +156,7 @@ const ERROR_MULTIPLE_URL_PAGE_MAILTO_DEFINITION = 1304;
 
 // Upload
 const ERROR_UPLOAD = 1400;
-const ERROR_DELETE_TMP_UPLOAD = 1401;
+const ERROR_UNKNOWN_ACTION = 1402;
 
 // KeyValueParser
 const ERROR_KVP_VALUE_HAS_NO_KEY = 1900;
@@ -207,6 +210,9 @@ const CLIENT_PAGE_TYPE = 'type';
 const CLIENT_PAGE_LANGUAGE = 'L';
 const CLIENT_UPLOAD_FE_NAME = 'name';
 
+const CLIENT_SIP_FOR_FORM = '_sipForForm';
+const CLIENT_FE_NAME = '_feName';
+
 // ALL $_SERVER variables: http://php.net/manual/en/reserved.variables.server.php
 // The following exist and might be the most used ones.
 const CLIENT_SCRIPT_URL = 'SCRIPT_URL';
@@ -386,5 +392,9 @@ const FILES_NAME = 'name';
 const FILES_TMP_NAME = 'tmp_name';
 const FILES_ERROR = 'error';
 const FILES_SIZE = 'size';
+const FILES_FLAG_DELETE = 'flagDelete';
 
-const UPLOAD_CACHED = '.cached';
\ No newline at end of file
+const UPLOAD_CACHED = '.cached';
+const FILE_ACTION = 'action';
+const FILE_ACTION_UPLOAD = 'upload';
+const FILE_ACTION_DELETE = 'delete';
\ No newline at end of file
diff --git a/extension/qfq/qfq/File.php b/extension/qfq/qfq/File.php
index 1c56bafcc1b7995246ece9bf5cdf79a6a432ddff..8bc59e7b9e068bf40b11b6e740bc52ce0ebf3527 100644
--- a/extension/qfq/qfq/File.php
+++ b/extension/qfq/qfq/File.php
@@ -16,13 +16,16 @@ class File {
     private $uploadErrMsg = array();
 
     /**
-     * @var null|Store
+     * @var Store
      */
     private $store = null;
 
     public function __construct($phpUnit = false) {
         $this->store = Store::getInstance('', $phpUnit);
 
+//        $sessionName = $this->store->getVar(SYSTEM_SESSION_NAME, STORE_SYSTEM);
+//        $this->sip = new Sip($sessionName);
+
         $this->uploadErrMsg = [
             UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini",
             UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form",
@@ -34,38 +37,74 @@ class File {
         ];
     }
 
+    /**
+     * @throws UserFormException
+     */
     public function process() {
 
-        $sipClient = $this->store->getVar(CLIENT_SIP, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
-        $sipStore = $this->store->getVar(SIP_SIP, STORE_SIP);
-        if ($sipClient === false || $sipStore === false || $sipClient !== $sipStore) {
-            throw new UserFormException('SIP invalid', ERROR_SIP_INVALID);
+        $sipUpload = $this->store->getVar(SIP_SIP, STORE_SIP);
+        if ($sipUpload === false) {
+            throw new UserFormException('SIP invalid: ' . $sipUpload, ERROR_SIP_INVALID);
+        }
+        $statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
+        if ($statusUpload === false) {
+            $statusUpload = array();
         }
 
-        $uploadFeName = $this->store->getVar(CLIENT_UPLOAD_FE_NAME, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
+        $action = $this->store->getVar(FILE_ACTION, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
+        switch ($action) {
+            case FILE_ACTION_UPLOAD:
+                $this->doUpload($sipUpload, $statusUpload);
+                break;
+            case FILE_ACTION_DELETE:
+                $this->doDelete($sipUpload, $statusUpload);
+                break;
+            default:
+                throw new UserFormException("Unknown FILE_ACTION: $action", ERROR_UNKNOWN_ACTION);
+        }
+    }
 
-        $keyStoreExtra = $sipStore . '-' . $uploadFeName;
+    /**
+     * @param string $keyStoreExtra
+     * @throws CodeException
+     * @throws UserFormException
+     */
+    private function doUpload($sipUpload, $statusUpload) {
 
         list($dummy, $newArr) = each($_FILES);
-        $oldArr = $this->store->getVar($keyStoreExtra, STORE_EXTRA, SANITIZE_ALLOW_ALL);
+        $statusUpload = array_merge($statusUpload, $newArr);
 
-        // Exist old cached upload? remove.
-        $filenameCached = Support::extendFilename($oldArr[FILES_TMP_NAME], UPLOAD_CACHED);
-
-        if (isset($oldArr[FILES_TMP_NAME]) && $oldArr[FILES_TMP_NAME] != '' && file_exists($filenameCached)) {
-            if (!unlink($oldArr[FILES_TMP_NAME] . '.cached')) {
-                throw new UserFormException("Failed to delete cached uploaded file: " . $filenameCached, ERROR_DELETE_TMP_UPLOAD);
-            }
-        }
-
-        if ($newArr[FILES_ERROR] !== UPLOAD_ERR_OK) {
+        if ($statusUpload[FILES_ERROR] !== UPLOAD_ERR_OK) {
             throw new UserFormException($this->uploadErrMsg[$newArr[FILES_ERROR]], ERROR_UPLOAD);
         }
 
-        $filenameCached = Support::extendFilename($newArr[FILES_TMP_NAME], UPLOAD_CACHED);
+        //TODO: do necessary checks with uploaded file HERE!!!
+
+
+        // rename uploaded file: ?.cached
+        $filenameCached = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
         move_uploaded_file($newArr[FILES_TMP_NAME], $filenameCached);
 
-        $this->store->setVar($keyStoreExtra, $newArr, STORE_EXTRA);
+        $this->store->setVar($sipUpload, $statusUpload, STORE_EXTRA);
     }
 
+    /**
+     * @param string $keyStoreExtra
+     * @throws CodeException
+     * @throws UserFormException
+     */
+    private function doDelete($sipUpload, $statusUpload) {
+
+        if (isset($statusUpload[FILES_TMP_NAME]) && $statusUpload[FILES_TMP_NAME] != '') {
+            $file = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
+            if (file_exists($file)) {
+                if (!unlink($file)) {
+                    throw new UserFormException('unlink file: ' . $file, ERROR_IO_UNLINK);
+                }
+            }
+            $statusUpload[FILES_TMP_NAME] = '';
+        }
+        $statusUpload[FILES_FLAG_DELETE] = '1';
+        $this->store->setVar($sipUpload, $statusUpload, STORE_EXTRA);
+    }
 }
\ No newline at end of file
diff --git a/extension/qfq/qfq/Save.php b/extension/qfq/qfq/Save.php
index 033917a74ec0b0bccc38c01f421822c7219ac727..ae8f9e66ade39b1ca153550a16a73301c231a66a 100644
--- a/extension/qfq/qfq/Save.php
+++ b/extension/qfq/qfq/Save.php
@@ -82,8 +82,11 @@ class Save {
         $tableColumns = array_keys($this->store->getStore(STORE_TABLE_COLUMN_TYPES));
         $formValues = $this->store->getStore(STORE_FORM);
 
+        $this->processAllUploads($recordId, $formValues);
+
         // Iterate over all table.columns. Built an assoc array $newValues.
         foreach ($tableColumns AS $column) {
+
             // Never save a predefined 'id': autoincrement values will be given by database..
             if ($column === 'id')
                 continue;
@@ -96,11 +99,8 @@ class Save {
             // Preparation for Log, Debug
             $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);
 
-            if (isset($formValues[$column])) {
-                $newValues[$column] = $formValues[$column];
-            } else {
-                $newValues[$column] = '';
-            }
+            Support::setIfNotSet($formValues, $column);
+            $newValues[$column] = $formValues[$column];
         }
 
         if ($recordId == 0) {
@@ -113,6 +113,75 @@ class Save {
         return $rc;
     }
 
+    /**
+     * Process all Upload Formelements for the given $recordId
+     *
+     * @param $recordId
+     * @param array $formValues
+     */
+    private function processAllUploads($recordId, array &$formValues) {
+
+        foreach ($this->feSpecNative AS $formElement) {
+            // skip non upload formElements
+            if ($formElement['type'] != 'upload') {
+                continue;
+            }
+
+            $column = $formElement['name'];
+            $file = $this->doUpload($formElement, $recordId, $formValues[$column]);
+            if ($file !== false) {
+                $formValues[$column] = $file;
+            }
+        }
+    }
+
+    /**
+     * Process upload for the given Formelement.
+     * - Check if there is an upload:
+     *   yes: do the upload
+     *   no: if there is an old file: delete it.
+     * @param $formElement
+     * @param $sipUpload
+     * @return string|false  New filename or false on error
+     * @throws CodeException
+     * @throws UserFormException
+     * @internal param $recordId
+     */
+    private function doUpload($formElement, $sipUpload) {
+
+        $statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
+        if ($statusUpload === false) {
+            return false;
+        }
+
+        // Delete existing old file.
+        if (isset($statusUpload[FILES_FLAG_DELETE]) && $statusUpload[FILES_FLAG_DELETE] == '1') {
+            $oldFile = $this->store->getVar($formElement['name'], STORE_RECORD);
+            if (file_exists($oldFile)) {
+                if (!unlink($oldFile)) {
+                    throw new UserFormException('unlink file: ' . $oldFile, ERROR_IO_UNLINK);
+                }
+            }
+        }
+
+        // TODO: calculate destination
+        $targetFile = $statusUpload[FILES_TMP_NAME] . '.final';
+
+        if (file_exists($targetFile)) {
+            throw new UserFormException('Copy upload failed - file already exist: ' . $targetFile, ERROR_IO_FILE_EXIST);
+        }
+
+        $uploadFile = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
+        if (!rename($uploadFile, $targetFile)) {
+            throw new UserFormException("Rename file: '$uploadFile' > '$targetFile'", ERROR_IO_RENAME);
+        }
+
+        $this->store->setVar($sipUpload, array(), STORE_EXTRA);
+
+        return $targetFile;
+
+    }
+
     /**
      * Get the complete FormElement for $name
      *
diff --git a/extension/qfq/qfq/store/Store.php b/extension/qfq/qfq/store/Store.php
index 143042b9db636de68268c224fd58a49f3ae716a0..94969d604ab6903c069c777bd632a8851fc35579 100644
--- a/extension/qfq/qfq/store/Store.php
+++ b/extension/qfq/qfq/store/Store.php
@@ -366,6 +366,7 @@ class Store {
      * @throws \qfq\CodeException
      */
     private static function fillStoreExtra() {
+
         if (isset($_SESSION[STORE_EXTRA]))
             self::setVarArray($_SESSION[STORE_EXTRA], STORE_EXTRA, true);
         else
@@ -454,9 +455,8 @@ class Store {
 
         // The STORE_EXTRA saves arrays and is persistent
         if ($store === STORE_EXTRA) {
-            foreach ($value as $k1 => $v1) {
-                $_SESSION[STORE_EXTRA][$key][$k1] = $v1;
-            }
+
+            $_SESSION[STORE_EXTRA][$key] = $value;
         }
 
     }