diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index f6689deb461e3a8e4e0617d2514d69985be5d4dd..d480ec17b4ad33498ca1a845ebc0fb1a622cb04a 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -42,13 +42,11 @@ set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/value.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/register_file_upload_handler.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/Client.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/UploadRequestHandler.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/DownloadRequestHandler.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileWriter.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileReader.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileLock.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileError.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/upload_request_handler.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/download_request_handler.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_writer.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_reader.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_error.h
     )
 
 # pass variable to parent scope
diff --git a/include/caosdb/file_transmission/Client.h b/include/caosdb/file_transmission/Client.h
deleted file mode 100644
index 3a2b4a8fc487112a4d89d845d0340620261c2fc4..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/Client.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "caosdb/entity.h"                        // for FileDescriptor
-#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for FileTransmissionS...
-#include "caosdb/handler_interface.h"             // for HandlerInterface
-#include "caosdb/status_code.h"                   // for StatusCode
-#include "caosdb/transaction_status.h"            // for StatusCode
-#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue
-#include <memory>                                 // for shared_ptr, uniqu...
-
-namespace caosdb::transaction {
-using caosdb::StatusCode;
-using caosdb::entity::FileDescriptor;
-using caosdb::entity::v1alpha1::FileTransmissionService;
-
-class FileExchangeClient final {
-public:
-  FileExchangeClient(
-    const std::shared_ptr<FileTransmissionService::Stub> &service)
-    : stub_(service) {}
-
-  ~FileExchangeClient();
-
-  FileExchangeClient(const FileExchangeClient &) = delete;
-  FileExchangeClient &operator=(const FileExchangeClient &) = delete;
-  FileExchangeClient(FileExchangeClient &&) = delete;
-  FileExchangeClient &operator=(FileExchangeClient &&) = delete;
-
-  StatusCode upload(const FileDescriptor &file_descriptor);
-  StatusCode download(const FileDescriptor &file_descriptor);
-
-  void Cancel();
-
-private:
-  int processMessages();
-
-  grpc::CompletionQueue cq_;
-
-  std::shared_ptr<FileTransmissionService::Stub> stub_;
-  std::unique_ptr<HandlerInterface> handler_;
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/DownloadRequestHandler.h b/include/caosdb/file_transmission/DownloadRequestHandler.h
deleted file mode 100644
index a59f9067605ac15aa01f569b8cc10ccadd6e7fef..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/DownloadRequestHandler.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "caosdb/entity.h"                        // for FileDescriptor
-#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for FileTransmissionS...
-#include "caosdb/entity/v1alpha1/main.pb.h"       // for FileDownloadResponse
-#include "caosdb/file_transmission/FileWriter.h"  // for FileWriter
-#include "caosdb/handler_interface.h"             // for HandlerTag, Handl...
-#include "caosdb/transaction_status.h"            // for TransactionStatus
-#include <grpcpp/impl/codegen/async_stream.h>     // for ClientAsyncReader
-#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
-#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue
-#include <grpcpp/impl/codegen/status.h>           // for Status
-#include <memory>                                 // for unique_ptr
-
-namespace caosdb::transaction {
-using caosdb::entity::FileDescriptor;
-using caosdb::entity::v1alpha1::FileDownloadRequest;
-using caosdb::entity::v1alpha1::FileDownloadResponse;
-using caosdb::entity::v1alpha1::FileTransmissionService;
-using caosdb::transaction::HandlerInterface;
-using caosdb::transaction::HandlerTag;
-
-class DownloadRequestHandler final : public HandlerInterface {
-public:
-  DownloadRequestHandler(HandlerTag tag, FileTransmissionService::Stub *stub,
-                         grpc::CompletionQueue *cq,
-                         FileDescriptor file_descriptor);
-
-  ~DownloadRequestHandler() override = default;
-
-  DownloadRequestHandler(const DownloadRequestHandler &) = delete;
-  DownloadRequestHandler &operator=(const DownloadRequestHandler &) = delete;
-  DownloadRequestHandler(DownloadRequestHandler &&) = delete;
-  DownloadRequestHandler &operator=(DownloadRequestHandler &&) = delete;
-
-  TransactionStatus GetStatus() override {
-    return TransactionStatus::EXECUTING();
-  }
-
-  void Start() override { OnNext(true); }
-
-  bool OnNext(bool ok) override;
-
-  void Cancel() override;
-
-protected:
-  enum class CallState { NewCall, SendingRequest, ReceivingFile, CallComplete };
-
-  void handleNewCallState();
-  void handleSendingRequestState();
-  void handleReceivingFileState();
-  void handleCallCompleteState();
-
-  HandlerTag tag_;
-
-  FileTransmissionService::Stub *stub_;
-  grpc::CompletionQueue *cq_;
-  grpc::ClientContext ctx_;
-
-  std::unique_ptr<grpc::ClientAsyncReader<FileDownloadResponse>> rpc_;
-
-  FileDownloadRequest *request_;
-  FileDownloadResponse *response_;
-  grpc::Status status_;
-
-  CallState state_;
-
-  std::unique_ptr<FileWriter> fileWriter_;
-
-  FileDescriptor file_descriptor_;
-
-  unsigned long long bytesReceived_;
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/FileError.h b/include/caosdb/file_transmission/FileError.h
deleted file mode 100644
index f9bae6083fc7f1db4791f82fee474302b77cd420..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/FileError.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <stdexcept>
-#include <string>
-
-namespace caosdb::transaction {
-
-class FileLockError : public std::runtime_error {
-public:
-  FileLockError(const std::string &message) : std::runtime_error(message) {}
-};
-
-class FileIOError : public std::runtime_error {
-public:
-  FileIOError(const std::string &message) : std::runtime_error(message) {}
-};
-
-class FileNotManagedError : public std::runtime_error {
-public:
-  FileNotManagedError(const std::string &message)
-    : std::runtime_error(message) {}
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/FileLock.h b/include/caosdb/file_transmission/FileLock.h
deleted file mode 100644
index 40092ea49e842f8848da40c38ee4b26135c846aa..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/FileLock.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include <mutex>
-#include <shared_mutex>
-
-namespace caosdb::transaction {
-
-using FileMutex = std::shared_timed_mutex;
-using FileReadLock = std::shared_lock<FileMutex>;
-using FileWriteLock = std::unique_lock<FileMutex>;
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/FileReader.h b/include/caosdb/file_transmission/FileReader.h
deleted file mode 100644
index 4ddca922cf5304239086840cb268b1205b505bb2..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/FileReader.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include "caosdb/file_transmission/FileLock.h" // for FileMutex, FileReadLock
-#include <boost/filesystem/fstream.hpp>        // for ifstream
-#include <boost/filesystem/operations.hpp>     // for exists
-#include <boost/filesystem/path.hpp>           // for path
-#include <fstream>                             // for ifstream, size_t
-#include <memory>                              // for shared_ptr
-#include <string>                              // for string
-
-namespace caosdb::transaction {
-using boost::filesystem::exists;
-using boost::filesystem::ifstream;
-using boost::filesystem::path;
-
-class FileReader final {
-public:
-  FileReader(boost::filesystem::path filename);
-  FileReader(boost::filesystem::path filename,
-             std::shared_ptr<FileMutex> mutexPtr);
-
-  ~FileReader() = default;
-
-  FileReader(const FileReader &) = delete;
-  FileReader &operator=(const FileReader &) = delete;
-
-  FileReader(FileReader &&) = default;
-  FileReader &operator=(FileReader &&) = default;
-
-  unsigned long long fileSize() const { return size_; }
-
-  std::size_t read(std::string &buffer);
-
-private:
-  void openFile();
-
-  std::ifstream stream_;
-  boost::filesystem::path filename_;
-  unsigned long long size_;
-
-  std::shared_ptr<FileMutex> mutexPtr_;
-  FileReadLock lock_;
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/FileWriter.h b/include/caosdb/file_transmission/FileWriter.h
deleted file mode 100644
index 0142d723910d361bee905c76c28d2157c7aebdb6..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/FileWriter.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include "caosdb/file_transmission/FileLock.h" // for FileMutex, FileWriteLock
-#include <boost/filesystem/path.hpp>           // for path
-#include <fstream>                             // for ofstream
-#include <memory>                              // for shared_ptr
-#include <string>                              // for string
-
-namespace caosdb::transaction {
-
-class FileWriter final {
-public:
-  FileWriter(boost::filesystem::path filename);
-  FileWriter(boost::filesystem::path filename,
-             std::shared_ptr<FileMutex> mutexPtr);
-
-  ~FileWriter() = default;
-
-  FileWriter(const FileWriter &) = delete;
-  FileWriter &operator=(const FileWriter &) = delete;
-
-  FileWriter(FileWriter &&) = default;
-  FileWriter &operator=(FileWriter &&) = default;
-
-  void write(const std::string &buffer);
-
-private:
-  void openFile();
-
-  std::ofstream stream_;
-  boost::filesystem::path filename_;
-
-  std::shared_ptr<FileMutex> mutexPtr_;
-  FileWriteLock lock_;
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/UploadRequestHandler.h b/include/caosdb/file_transmission/UploadRequestHandler.h
deleted file mode 100644
index 8ee96c8a2875162bbbb615fdb2d30a2e47f82370..0000000000000000000000000000000000000000
--- a/include/caosdb/file_transmission/UploadRequestHandler.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#include "caosdb/entity.h"                        // for FileDescriptor
-#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for FileTransmissionS...
-#include "caosdb/entity/v1alpha1/main.pb.h"       // for FileUploadRequest
-#include "caosdb/file_transmission/FileReader.h"  // for FileReader
-#include "caosdb/handler_interface.h"             // for HandlerTag, Handl...
-#include "caosdb/transaction_status.h"            // for TransactionStatus
-#include <cstdint>                                // for uint64_t
-#include <grpcpp/impl/codegen/async_stream.h>     // for ClientAsyncWriter
-#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
-#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue
-#include <grpcpp/impl/codegen/status.h>           // for Status
-#include <memory>                                 // for unique_ptr
-
-namespace caosdb::transaction {
-using caosdb::entity::FileDescriptor;
-using caosdb::entity::v1alpha1::FileTransmissionService;
-using caosdb::entity::v1alpha1::FileUploadRequest;
-using caosdb::entity::v1alpha1::FileUploadResponse;
-using caosdb::transaction::HandlerInterface;
-using caosdb::transaction::HandlerTag;
-
-class UploadRequestHandler final : public HandlerInterface {
-public:
-  UploadRequestHandler(HandlerTag tag, FileTransmissionService::Stub *stub,
-                       grpc::CompletionQueue *cq,
-                       FileDescriptor file_descriptor);
-
-  ~UploadRequestHandler() override = default;
-
-  UploadRequestHandler(const UploadRequestHandler &) = delete;
-  UploadRequestHandler &operator=(const UploadRequestHandler &) = delete;
-  UploadRequestHandler(UploadRequestHandler &&) = delete;
-  UploadRequestHandler &operator=(UploadRequestHandler &&) = delete;
-
-  TransactionStatus GetStatus() override {
-    return TransactionStatus::EXECUTING();
-  }
-
-  void Start() override { OnNext(true); }
-
-  bool OnNext(bool ok) override;
-
-  void Cancel() override;
-
-protected:
-  enum class CallState {
-    NewCall,
-    SendingHeader,
-    SendingFile,
-    ExpectingResponse,
-    CallComplete
-  };
-
-  void handleNewCallState();
-  void handleSendingHeaderState();
-  void handleSendingFileState();
-  void handleExpectingResponseState();
-  void handleCallCompleteState();
-
-  HandlerTag tag_;
-
-  FileTransmissionService::Stub *stub_;
-  grpc::CompletionQueue *cq_;
-  grpc::ClientContext ctx_;
-
-  std::unique_ptr<grpc::ClientAsyncWriter<FileUploadRequest>> rpc_;
-
-  FileUploadRequest *request_;
-  FileUploadResponse *response_;
-  grpc::Status status_;
-
-  CallState state_;
-
-  std::unique_ptr<FileReader> fileReader_;
-
-  FileDescriptor file_descriptor_;
-
-  uint64_t bytesToSend_;
-};
-
-} // namespace caosdb::transaction
diff --git a/include/caosdb/file_transmission/download_request_handler.h b/include/caosdb/file_transmission/download_request_handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..c6ee1c36b67c637c8faad901f590a4eee318b7b5
--- /dev/null
+++ b/include/caosdb/file_transmission/download_request_handler.h
@@ -0,0 +1,121 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_DOWNLOAD_REQUEST_HANDLER_H
+#define CAOSDB_FILE_TRANSMISSION_DOWNLOAD_REQUEST_HANDLER_H
+
+#include "caosdb/entity.h"                        // for FileDescriptor
+#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for FileTransmissionS...
+#include "caosdb/entity/v1alpha1/main.pb.h"       // for FileDownloadResponse
+#include "caosdb/file_transmission/file_writer.h" // for FileWriter
+#include "caosdb/handler_interface.h"             // for HandlerTag, Handl...
+#include <grpcpp/impl/codegen/async_stream.h>     // for ClientAsyncReader
+#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
+#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue
+#include <grpcpp/impl/codegen/status.h>           // for Status
+#include <memory>                                 // for unique_ptr
+
+namespace caosdb::transaction {
+using caosdb::entity::FileDescriptor;
+using caosdb::entity::v1alpha1::FileDownloadRequest;
+using caosdb::entity::v1alpha1::FileDownloadResponse;
+using caosdb::entity::v1alpha1::FileTransmissionService;
+using caosdb::transaction::HandlerInterface;
+using caosdb::transaction::HandlerTag;
+
+class DownloadRequestHandler final : public HandlerInterface {
+public:
+  DownloadRequestHandler(HandlerTag tag, FileTransmissionService::Stub *stub,
+                         grpc::CompletionQueue *cq,
+                         FileDescriptor file_descriptor);
+
+  ~DownloadRequestHandler() override = default;
+
+  DownloadRequestHandler(const DownloadRequestHandler &) = delete;
+  DownloadRequestHandler &operator=(const DownloadRequestHandler &) = delete;
+  DownloadRequestHandler(DownloadRequestHandler &&) = delete;
+  DownloadRequestHandler &operator=(DownloadRequestHandler &&) = delete;
+
+  void Start() override { OnNext(true); }
+
+  bool OnNext(bool ok) override;
+
+  void Cancel() override;
+
+protected:
+  enum class CallState { NewCall, SendingRequest, ReceivingFile, CallComplete };
+
+  void handleNewCallState();
+  void handleSendingRequestState();
+  void handleReceivingFileState();
+  void handleCallCompleteState();
+
+  HandlerTag tag_;
+
+  FileTransmissionService::Stub *stub_;
+  grpc::CompletionQueue *cq_;
+  grpc::ClientContext ctx_;
+
+  std::unique_ptr<grpc::ClientAsyncReader<FileDownloadResponse>> rpc_;
+
+  FileDownloadRequest *request_;
+  FileDownloadResponse *response_;
+  grpc::Status status_;
+
+  CallState state_;
+
+  std::unique_ptr<FileWriter> fileWriter_;
+
+  FileDescriptor file_descriptor_;
+
+  unsigned long long bytesReceived_;
+};
+
+} // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/file_transmission/file_error.h b/include/caosdb/file_transmission/file_error.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae057be3ba47f894f8fdbc22adb6e6d56accdafd
--- /dev/null
+++ b/include/caosdb/file_transmission/file_error.h
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_FILE_ERROR_H
+#define CAOSDB_FILE_TRANSMISSION_FILE_ERROR_H
+
+#include <stdexcept>
+#include <string>
+
+namespace caosdb::transaction {
+
+class FileIOError : public std::runtime_error {
+public:
+  FileIOError(const std::string &message) : std::runtime_error(message) {}
+};
+
+} // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/file_transmission/file_reader.h b/include/caosdb/file_transmission/file_reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..67c1247fa97cc43e28064b4e0da81812f41b231a
--- /dev/null
+++ b/include/caosdb/file_transmission/file_reader.h
@@ -0,0 +1,89 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_FILE_READER_H
+#define CAOSDB_FILE_TRANSMISSION_FILE_READER_H
+
+#include <boost/filesystem/fstream.hpp>    // for ifstream
+#include <boost/filesystem/operations.hpp> // for exists
+#include <boost/filesystem/path.hpp>       // for path
+#include <fstream>                         // for ifstream, size_t
+#include <string>                          // for string
+
+namespace caosdb::transaction {
+using boost::filesystem::exists;
+using boost::filesystem::ifstream;
+using boost::filesystem::path;
+
+class FileReader final {
+public:
+  FileReader(boost::filesystem::path filename);
+
+  ~FileReader() = default;
+
+  FileReader(const FileReader &) = delete;
+  FileReader &operator=(const FileReader &) = delete;
+
+  FileReader(FileReader &&) = default;
+  FileReader &operator=(FileReader &&) = default;
+
+  unsigned long long fileSize() const { return size_; }
+
+  std::size_t read(std::string &buffer);
+
+private:
+  void openFile();
+
+  std::ifstream stream_;
+  boost::filesystem::path filename_;
+  unsigned long long size_;
+};
+
+} // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/file_transmission/file_writer.h b/include/caosdb/file_transmission/file_writer.h
new file mode 100644
index 0000000000000000000000000000000000000000..801d74b9547951d2a3b86ed4b333bfb4b7035aa9
--- /dev/null
+++ b/include/caosdb/file_transmission/file_writer.h
@@ -0,0 +1,81 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_FILE_WRITER_H
+#define CAOSDB_FILE_TRANSMISSION_FILE_WRITER_H
+
+#include <boost/filesystem/path.hpp> // for path
+#include <fstream>                   // for ofstream
+#include <string>                    // for string
+
+namespace caosdb::transaction {
+
+class FileWriter final {
+public:
+  FileWriter(boost::filesystem::path filename);
+
+  ~FileWriter() = default;
+
+  FileWriter(const FileWriter &) = delete;
+  FileWriter &operator=(const FileWriter &) = delete;
+
+  FileWriter(FileWriter &&) = default;
+  FileWriter &operator=(FileWriter &&) = default;
+
+  void write(const std::string &buffer);
+
+private:
+  void openFile();
+
+  std::ofstream stream_;
+  boost::filesystem::path filename_;
+};
+
+} // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/file_transmission/register_file_upload_handler.h b/include/caosdb/file_transmission/register_file_upload_handler.h
index 0d41b947eebf92b50ac6b33b97f0dc67bf6bad9c..17a42c0a2a2cf1d591048abb6e4c8b329bfd008c 100644
--- a/include/caosdb/file_transmission/register_file_upload_handler.h
+++ b/include/caosdb/file_transmission/register_file_upload_handler.h
@@ -1,4 +1,54 @@
-#pragma once
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_REGISTER_FILE_UPLOAD_H
+#define CAOSDB_FILE_TRANSMISSION_REGISTER_FILE_UPLOAD_H
+
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for FileTransmissionS...
 #include "caosdb/entity/v1alpha1/main.pb.h"      // for FileDownloadResponse
 #include "caosdb/handler_interface.h"            // for HandlerTag, Handl...
@@ -30,7 +80,6 @@ public:
 
 protected:
   void handleNewCallState() override;
-  void handleReceivingFileState() override;
 
   HandlerTag tag_;
 
@@ -44,3 +93,5 @@ protected:
 };
 
 } // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/file_transmission/upload_request_handler.h b/include/caosdb/file_transmission/upload_request_handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..54621d12ba7d67d374037ecdb1f1259662071319
--- /dev/null
+++ b/include/caosdb/file_transmission/upload_request_handler.h
@@ -0,0 +1,129 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_FILE_TRANSMISSION_UPLOAD_REQUEST_HANDLER_H
+#define CAOSDB_FILE_TRANSMISSION_UPLOAD_REQUEST_HANDLER_H
+
+#include "caosdb/entity.h"                        // for FileDescriptor
+#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for FileTransmissionS...
+#include "caosdb/entity/v1alpha1/main.pb.h"       // for FileUploadRequest
+#include "caosdb/file_transmission/file_reader.h" // for FileReader
+#include "caosdb/handler_interface.h"             // for HandlerTag, Handl...
+#include <cstdint>                                // for uint64_t
+#include <grpcpp/impl/codegen/async_stream.h>     // for ClientAsyncWriter
+#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
+#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue
+#include <grpcpp/impl/codegen/status.h>           // for Status
+#include <memory>                                 // for unique_ptr
+
+namespace caosdb::transaction {
+using caosdb::entity::FileDescriptor;
+using caosdb::entity::v1alpha1::FileTransmissionService;
+using caosdb::entity::v1alpha1::FileUploadRequest;
+using caosdb::entity::v1alpha1::FileUploadResponse;
+using caosdb::transaction::HandlerInterface;
+using caosdb::transaction::HandlerTag;
+
+class UploadRequestHandler final : public HandlerInterface {
+public:
+  UploadRequestHandler(HandlerTag tag, FileTransmissionService::Stub *stub,
+                       grpc::CompletionQueue *cq,
+                       FileDescriptor file_descriptor);
+
+  ~UploadRequestHandler() override = default;
+
+  UploadRequestHandler(const UploadRequestHandler &) = delete;
+  UploadRequestHandler &operator=(const UploadRequestHandler &) = delete;
+  UploadRequestHandler(UploadRequestHandler &&) = delete;
+  UploadRequestHandler &operator=(UploadRequestHandler &&) = delete;
+
+  void Start() override { OnNext(true); }
+
+  bool OnNext(bool ok) override;
+
+  void Cancel() override;
+
+protected:
+  enum class CallState {
+    NewCall,
+    SendingHeader,
+    SendingFile,
+    ExpectingResponse,
+    CallComplete
+  };
+
+  void handleNewCallState();
+  void handleSendingHeaderState();
+  void handleSendingFileState();
+  void handleExpectingResponseState();
+  void handleCallCompleteState();
+
+  HandlerTag tag_;
+
+  FileTransmissionService::Stub *stub_;
+  grpc::CompletionQueue *cq_;
+  grpc::ClientContext ctx_;
+
+  std::unique_ptr<grpc::ClientAsyncWriter<FileUploadRequest>> rpc_;
+
+  FileUploadRequest *request_;
+  FileUploadResponse *response_;
+  grpc::Status status_;
+
+  CallState state_;
+
+  std::unique_ptr<FileReader> fileReader_;
+
+  FileDescriptor file_descriptor_;
+
+  uint64_t bytesToSend_;
+};
+
+} // namespace caosdb::transaction
+
+#endif
diff --git a/include/caosdb/handler_interface.h b/include/caosdb/handler_interface.h
index ff1a42b047cc7f216cacc63c945e97adb709c66c..4ba563a300175478c8140e9b88c991e2f5321245 100644
--- a/include/caosdb/handler_interface.h
+++ b/include/caosdb/handler_interface.h
@@ -59,6 +59,8 @@ const static std::string logger_name = "caosdb::transaction";
 
 class HandlerInterface {
 public:
+  HandlerInterface() : transaction_status(TransactionStatus::READY()) {}
+
   virtual ~HandlerInterface() = default;
 
   virtual void Start() = 0;
@@ -67,7 +69,10 @@ public:
 
   virtual void Cancel() = 0;
 
-  virtual TransactionStatus GetStatus() = 0;
+  inline TransactionStatus GetStatus() { return this->transaction_status; }
+
+protected:
+  TransactionStatus transaction_status;
 };
 
 using HandlerPtr = std::unique_ptr<HandlerInterface>;
diff --git a/include/caosdb/transaction_handler.h b/include/caosdb/transaction_handler.h
index 69df4f0b370c5cdcc9515a916e36c1c899fd3397..0a7154d03dfbde19284807d1e1f40998c89e1de0 100644
--- a/include/caosdb/transaction_handler.h
+++ b/include/caosdb/transaction_handler.h
@@ -30,7 +30,6 @@ public:
 
 protected:
   virtual void handleNewCallState() override;
-  virtual void handleReceivingFileState() override;
 
   HandlerTag tag_;
 
diff --git a/include/caosdb/unary_rpc_handler.h b/include/caosdb/unary_rpc_handler.h
index 816bd65683eeee0d5aebf34abb386c76cbb544fb..6955504a2baea38cb0bd8075bdca2d20c9f8a52a 100644
--- a/include/caosdb/unary_rpc_handler.h
+++ b/include/caosdb/unary_rpc_handler.h
@@ -1,4 +1,54 @@
-#pragma once
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAOSDB_UNARY_RPC_HANDLER_H
+#define CAOSDB_UNARY_RPC_HANDLER_H
+
 #include "caosdb/handler_interface.h"             // for HandlerTag, Handl...
 #include "caosdb/transaction_status.h"            // for TransactionStatus
 #include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
@@ -10,8 +60,8 @@ namespace caosdb::transaction {
 class UnaryRpcHandler : public HandlerInterface {
 public:
   inline UnaryRpcHandler(grpc::CompletionQueue *completion_queue)
-    : state_(CallState::NewCall), completion_queue(completion_queue),
-      transaction_status(TransactionStatus::EXECUTING()) {}
+    : HandlerInterface(), state_(CallState::NewCall),
+      completion_queue(completion_queue) {}
 
   void Start() override {
     transaction_status = TransactionStatus::EXECUTING();
@@ -22,20 +72,18 @@ public:
 
   void Cancel() override;
 
-  TransactionStatus GetStatus() override { return transaction_status; }
-
 protected:
   virtual void handleNewCallState() = 0;
-  virtual void handleReceivingFileState() = 0;
   void handleCallCompleteState();
 
-  enum class CallState { NewCall, ReceivingFile, CallComplete };
+  enum class CallState { NewCall, CallComplete };
   CallState state_;
   grpc::CompletionQueue *completion_queue;
 
   grpc::ClientContext call_context;
   grpc::Status status_;
-  TransactionStatus transaction_status;
 };
 
 } // namespace caosdb::transaction
+
+#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 366875d669b57c4df3942aec5fa4658873754a86..89180ee81fc6b07d4f081c90fd1200530a2d60b6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,11 +31,10 @@ set(libcaosdb_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction_handler.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/unary_rpc_handler.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/register_file_upload_handler.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/Client.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/UploadRequestHandler.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/DownloadRequestHandler.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileWriter.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/FileReader.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/upload_request_handler.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/download_request_handler.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_writer.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_reader.cpp
     )
 
 # pass variable to parent scope
diff --git a/src/caosdb/file_transmission/Client.cpp b/src/caosdb/file_transmission/Client.cpp
deleted file mode 100644
index 9adabd60806537c35ac882fa601723a2cad37464..0000000000000000000000000000000000000000
--- a/src/caosdb/file_transmission/Client.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#include "caosdb/file_transmission/Client.h"
-#include "caosdb/file_transmission/DownloadRequestHandler.h" // for DownloadReq...
-#include "caosdb/file_transmission/UploadRequestHandler.h" // for UploadReque...
-#include "caosdb/logging.h"                                // for CAOSDB_LOG_...
-#include "caosdb/status_code.h"                            // for StatusCode
-#include <boost/log/core/record.hpp>                       // for record
-#include <boost/log/sources/record_ostream.hpp>            // for basic_recor...
-#include <boost/preprocessor/seq/limits/enum_256.hpp>      // for BOOST_PP_SE...
-#include <boost/preprocessor/seq/limits/size_256.hpp>      // for BOOST_PP_SE...
-#include <exception>                                       // IWYU pragma: keep
-// IWYU pragma: no_include <bits/exception.h>
-#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQ...
-
-namespace caosdb::transaction {
-using caosdb::StatusCode;
-
-FileExchangeClient::~FileExchangeClient() {
-  this->Cancel();
-
-  cq_.Shutdown();
-
-  // drain the queue
-  void *ignoredTag = nullptr;
-  bool ok = false;
-  while (cq_.Next(&ignoredTag, &ok)) {
-    ;
-  }
-}
-
-StatusCode FileExchangeClient::upload(const FileDescriptor &file_descriptor) {
-  handler_ = std::make_unique<UploadRequestHandler>(&handler_, stub_.get(),
-                                                    &cq_, file_descriptor);
-
-  int status = this->processMessages();
-  if (status > 0) {
-    return StatusCode::FILE_UPLOAD_ERROR;
-  }
-  return StatusCode::SUCCESS;
-}
-
-StatusCode FileExchangeClient::download(const FileDescriptor &file_descriptor) {
-  handler_ = std::make_unique<DownloadRequestHandler>(&handler_, stub_.get(),
-                                                      &cq_, file_descriptor);
-
-  int status = this->processMessages();
-  if (status > 0) {
-    return StatusCode::FILE_DOWNLOAD_ERROR;
-  }
-  return StatusCode::SUCCESS;
-}
-
-void FileExchangeClient::Cancel() {
-  if (handler_) {
-    handler_->Cancel();
-  }
-}
-
-int FileExchangeClient::processMessages() {
-  try {
-    handler_->Start();
-    void *tag = nullptr;
-    bool ok = false;
-    while (true) {
-      if (cq_.Next(&tag, &ok)) {
-        if (tag != nullptr) {
-          // TODO(tf): assert
-          auto res = handler_->OnNext(ok);
-          if (!res) {
-            // TODO(tf): comment
-            handler_.reset();
-            break;
-          }
-        } else {
-          CAOSDB_LOG_ERROR(logger_name)
-            << "Invalid tag delivered by notification queue.";
-        }
-      } else {
-        CAOSDB_LOG_ERROR(logger_name)
-          << "Notification queue has been shut down unexpectedly.";
-        return 1;
-      }
-    }
-  } catch (std::exception &e) {
-    CAOSDB_LOG_ERROR(logger_name) << "Caught exception: " << e.what();
-    return 1;
-  } catch (...) {
-    CAOSDB_LOG_ERROR(logger_name) << "Caught unknown exception.";
-    return 1;
-  }
-  return 0;
-}
-
-} // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/FileReader.cpp b/src/caosdb/file_transmission/FileReader.cpp
deleted file mode 100644
index 5124846c5f993fa3311c72f3445f53ea5482bba7..0000000000000000000000000000000000000000
--- a/src/caosdb/file_transmission/FileReader.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "caosdb/file_transmission/FileReader.h"
-#include "caosdb/file_transmission/FileError.h" // for FileIOError, FileLockError
-#include <boost/filesystem/path.hpp>            // for path
-#include <mutex>                                // for try_to_lock
-#include <utility>                              // for move
-
-namespace caosdb::transaction {
-
-FileReader::FileReader(boost::filesystem::path filename)
-  : filename_(std::move(filename)), size_(0) {
-  this->openFile();
-}
-
-FileReader::FileReader(boost::filesystem::path filename,
-                       std::shared_ptr<FileMutex> mutexPtr)
-  : filename_(std::move(filename)), size_(0), mutexPtr_(std::move(mutexPtr)) {
-  this->openFile();
-}
-
-void FileReader::openFile() {
-  if (mutexPtr_) {
-    lock_ = FileReadLock(*mutexPtr_, std::try_to_lock);
-    if (!lock_) {
-      throw FileLockError("Can't lock file for reading: " + filename_.string());
-    }
-  }
-
-  stream_.open(filename_, std::ios::binary | std::ios::ate);
-  if (!stream_) {
-    throw FileIOError("Can't open file for reading: " + filename_.string());
-  }
-
-  auto size = stream_.tellg();
-  stream_.seekg(0);
-  if (size > 0) {
-    size_ = static_cast<decltype(size_)>(size);
-  }
-}
-
-std::size_t FileReader::read(std::string &buffer) {
-  std::size_t bytesRead = 0;
-
-  if (!stream_.eof()) {
-    auto bufferSize = buffer.size();
-    if (bufferSize > 0) {
-      if (!stream_.read(&buffer[0], bufferSize)) {
-        throw FileIOError("Can't read file: " + filename_.string());
-      }
-
-      bytesRead = static_cast<std::size_t>(stream_.gcount());
-    }
-  }
-
-  return bytesRead;
-}
-
-} // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/FileWriter.cpp b/src/caosdb/file_transmission/FileWriter.cpp
deleted file mode 100644
index 3d89c1c6f2343ca5a39a167ad2dc88ff5a4e3aa2..0000000000000000000000000000000000000000
--- a/src/caosdb/file_transmission/FileWriter.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "caosdb/file_transmission/FileWriter.h"
-#include "caosdb/file_transmission/FileError.h" // for FileIOError, FileLockError
-#include <boost/filesystem/path.hpp>            // for path
-#include <mutex>                                // for try_to_lock
-#include <utility>                              // for move
-
-namespace caosdb::transaction {
-
-FileWriter::FileWriter(boost::filesystem::path filename)
-  : filename_(std::move(filename)) {
-  this->openFile();
-}
-
-FileWriter::FileWriter(boost::filesystem::path filename,
-                       std::shared_ptr<FileMutex> mutexPtr)
-  : filename_(std::move(filename)), mutexPtr_(std::move(mutexPtr)) {
-  this->openFile();
-}
-
-void FileWriter::openFile() {
-  if (mutexPtr_) {
-    lock_ = FileWriteLock(*mutexPtr_, std::try_to_lock);
-    if (!lock_) {
-      throw FileLockError("Can't lock file for writing: " + filename_.string());
-    }
-  }
-
-  stream_.open(filename_, std::ios::binary | std::ios::trunc);
-  if (!stream_) {
-    throw FileIOError("Can't open file for writing: " + filename_.string());
-  }
-}
-
-void FileWriter::write(const std::string &buffer) {
-  auto bufferSize = buffer.size();
-  if (bufferSize > 0) {
-    if (!stream_.write(buffer.data(), bufferSize)) {
-      throw FileIOError("Can't write file: " + filename_.string());
-    }
-  }
-}
-
-} // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/DownloadRequestHandler.cpp b/src/caosdb/file_transmission/download_request_handler.cpp
similarity index 57%
rename from src/caosdb/file_transmission/DownloadRequestHandler.cpp
rename to src/caosdb/file_transmission/download_request_handler.cpp
index 87730febcf8589f30639123a18a65d60df05de90..fb36ca5b46cbf2c4f08c2c06bc9faf6b00e038be 100644
--- a/src/caosdb/file_transmission/DownloadRequestHandler.cpp
+++ b/src/caosdb/file_transmission/download_request_handler.cpp
@@ -1,8 +1,56 @@
-#include "caosdb/file_transmission/DownloadRequestHandler.h"
-#include "caosdb/exceptions.h"                         // for Exception
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#include "caosdb/file_transmission/download_request_handler.h"
 #include "caosdb/logging.h"                            // for CAOSDB_LOG_TRACE
 #include "caosdb/protobuf_helper.h"                    // for get_arena
 #include "caosdb/status_code.h"                        // for GENERIC_RPC_E...
+#include "caosdb/transaction_status.h"                 // for TransactionStatus
 #include <boost/filesystem/path.hpp>                   // for operator<<, path
 #include <boost/log/core/record.hpp>                   // for record
 #include <boost/log/detail/attachable_sstream_buf.hpp> // for basic_ostring...
@@ -24,9 +72,6 @@
 
 namespace caosdb::transaction {
 using caosdb::StatusCode;
-using caosdb::exceptions::AuthenticationError;
-using caosdb::exceptions::ConnectionError;
-using caosdb::exceptions::Exception;
 using caosdb::utility::get_arena;
 using google::protobuf::Arena;
 
@@ -50,7 +95,7 @@ bool DownloadRequestHandler::OnNext(bool ok) {
         this->handleReceivingFileState();
       } else if (state_ == CallState::CallComplete) {
         this->handleCallCompleteState();
-        return false; // TODO(tf): comment
+        return false;
       }
     } else {
       state_ = CallState::CallComplete;
@@ -58,19 +103,20 @@ bool DownloadRequestHandler::OnNext(bool ok) {
     }
 
     return true;
-  } catch (Exception &e) {
-    throw;
   } catch (std::exception &e) {
-    CAOSDB_LOG_ERROR(logger_name) << "Download processing error: " << e.what();
-    throw;
+    CAOSDB_LOG_ERROR(logger_name)
+      << "DownloadRequestHandler caught an exception: " << e.what();
+    transaction_status = TransactionStatus::GENERIC_ERROR(e.what());
+    state_ = CallState::CallComplete;
   } catch (...) {
     CAOSDB_LOG_ERROR(logger_name)
-      << "Download processing error: unknown exception caught";
-    throw;
+      << "Transaction error: unknown exception caught";
+    transaction_status = TransactionStatus::GENERIC_ERROR(
+      "DownloadRequestHandler caught an unknown exception");
+    state_ = CallState::CallComplete;
   }
 
   if (state_ == CallState::NewCall) {
-    // TODO(tf): comment
     return false;
   }
 
@@ -93,6 +139,7 @@ void DownloadRequestHandler::handleNewCallState() {
 
   rpc_ = stub_->PrepareAsyncFileDownload(&ctx_, *request_, cq_);
 
+  transaction_status = TransactionStatus::EXECUTING();
   state_ = CallState::SendingRequest;
   rpc_->StartCall(tag_);
   CAOSDB_LOG_TRACE(logger_name)
@@ -133,22 +180,26 @@ void DownloadRequestHandler::handleReceivingFileState() {
 void DownloadRequestHandler::handleCallCompleteState() {
   CAOSDB_LOG_TRACE(logger_name)
     << "Enter DownloadRequestHandler::handleCallCompleteState";
+
   switch (status_.error_code()) {
-  case grpc::OK:
-    CAOSDB_LOG_INFO(logger_name) << "[" << file_descriptor_.local_path
-                                 << "]: download complete: " << bytesReceived_
-                                 << " bytes received" << std::endl;
-    break;
-
-  case grpc::UNAUTHENTICATED:
-    throw AuthenticationError(status_.error_message());
-  case grpc::UNAVAILABLE:
-    throw ConnectionError(status_.error_message());
-  default:
-    throw Exception(StatusCode::GENERIC_RPC_ERROR,
-                    "GRPC error code " + std::to_string(status_.error_code()) +
-                      " - " + status_.error_message());
+  case grpc::OK: {
+    CAOSDB_LOG_INFO(logger_name)
+      << "DownloadRequestHandler finished successfully ("
+      << file_descriptor_.local_path << "): Download complete, "
+      << bytesReceived_ << " bytes received.";
+  } break;
+  default: {
+    auto code(static_cast<StatusCode>(status_.error_code()));
+    std::string description(get_status_description(code) +
+                            " Original message: " + status_.error_message());
+    transaction_status = TransactionStatus(code, description);
+    CAOSDB_LOG_ERROR(logger_name)
+      << "DownloadRequestHandler finished with an error ("
+      << file_descriptor_.local_path << "): Download aborted with code " << code
+      << " - " << description;
+  } break;
   }
+
   CAOSDB_LOG_TRACE(logger_name)
     << "Leave DownloadRequestHandler::handleCallCompleteState";
 }
diff --git a/src/caosdb/file_transmission/file_reader.cpp b/src/caosdb/file_transmission/file_reader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2df58c9accbde99c9bc908ed7f80ceef7685b573
--- /dev/null
+++ b/src/caosdb/file_transmission/file_reader.cpp
@@ -0,0 +1,91 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#include "caosdb/file_transmission/file_reader.h"
+#include "caosdb/file_transmission/file_error.h" // for FileIOError
+#include <boost/filesystem/path.hpp>             // for path
+#include <utility>                               // for move
+
+namespace caosdb::transaction {
+
+FileReader::FileReader(boost::filesystem::path filename)
+  : filename_(std::move(filename)), size_(0) {
+  this->openFile();
+}
+
+void FileReader::openFile() {
+  stream_.open(filename_, std::ios::binary | std::ios::ate);
+  if (!stream_) {
+    throw FileIOError("Can't open file for reading: " + filename_.string());
+  }
+
+  auto size = stream_.tellg();
+  stream_.seekg(0);
+  if (size > 0) {
+    size_ = static_cast<decltype(size_)>(size);
+  }
+}
+
+std::size_t FileReader::read(std::string &buffer) {
+  std::size_t bytesRead = 0;
+
+  if (!stream_.eof()) {
+    auto bufferSize = buffer.size();
+    if (bufferSize > 0) {
+      if (!stream_.read(&buffer[0], bufferSize)) {
+        throw FileIOError("Can't read file: " + filename_.string());
+      }
+
+      bytesRead = static_cast<std::size_t>(stream_.gcount());
+    }
+  }
+
+  return bytesRead;
+}
+
+} // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/file_writer.cpp b/src/caosdb/file_transmission/file_writer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73b604d4255b213d015e010ea987fdb6a9918f6c
--- /dev/null
+++ b/src/caosdb/file_transmission/file_writer.cpp
@@ -0,0 +1,77 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#include "caosdb/file_transmission/file_writer.h"
+#include "caosdb/file_transmission/file_error.h" // for FileIOError
+#include <boost/filesystem/path.hpp>             // for path
+#include <utility>                               // for move
+
+namespace caosdb::transaction {
+
+FileWriter::FileWriter(boost::filesystem::path filename)
+  : filename_(std::move(filename)) {
+  this->openFile();
+}
+
+void FileWriter::openFile() {
+  stream_.open(filename_, std::ios::binary | std::ios::trunc);
+  if (!stream_) {
+    throw FileIOError("Can't open file for writing: " + filename_.string());
+  }
+}
+
+void FileWriter::write(const std::string &buffer) {
+  auto bufferSize = buffer.size();
+  if (bufferSize > 0) {
+    if (!stream_.write(buffer.data(), bufferSize)) {
+      throw FileIOError("Can't write file: " + filename_.string());
+    }
+  }
+}
+
+} // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/register_file_upload_handler.cpp b/src/caosdb/file_transmission/register_file_upload_handler.cpp
index 2661756cdc565516a7ec6dfa74fde36bf2f1cc7e..b2020223f1c79f69e77b1e0950bac1c8f1220986 100644
--- a/src/caosdb/file_transmission/register_file_upload_handler.cpp
+++ b/src/caosdb/file_transmission/register_file_upload_handler.cpp
@@ -1,3 +1,51 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
 #include "caosdb/file_transmission/register_file_upload_handler.h"
 #include "caosdb/logging.h"                           // for CAOSDB_LOG_TRACE
 #include <boost/log/core/record.hpp>                  // for record
@@ -33,6 +81,4 @@ void RegisterFileUploadHandler::handleNewCallState() {
     << "Leave RegisterFileUploadHandler::handleNewCallState";
 }
 
-void RegisterFileUploadHandler::handleReceivingFileState() {}
-
 } // namespace caosdb::transaction
diff --git a/src/caosdb/file_transmission/UploadRequestHandler.cpp b/src/caosdb/file_transmission/upload_request_handler.cpp
similarity index 56%
rename from src/caosdb/file_transmission/UploadRequestHandler.cpp
rename to src/caosdb/file_transmission/upload_request_handler.cpp
index c93ca3ab29d36bc752fb0bf5dae733b07e0bbeff..cffe7577be41229110b1f6467615bf62cc629fa7 100644
--- a/src/caosdb/file_transmission/UploadRequestHandler.cpp
+++ b/src/caosdb/file_transmission/upload_request_handler.cpp
@@ -1,8 +1,56 @@
-#include "caosdb/file_transmission/UploadRequestHandler.h"
-#include "caosdb/exceptions.h"                         // for Exception
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
+#include "caosdb/file_transmission/upload_request_handler.h"
 #include "caosdb/logging.h"                            // for CAOSDB_LOG_ERROR
 #include "caosdb/protobuf_helper.h"                    // for get_arena
 #include "caosdb/status_code.h"                        // for GENERIC_RPC_E...
+#include "caosdb/transaction_status.h"                 // for TransactionStatus
 #include <algorithm>                                   // for min
 #include <boost/filesystem/path.hpp>                   // for operator<<, path
 #include <boost/log/core/record.hpp>                   // for record
@@ -27,9 +75,6 @@
 
 namespace caosdb::transaction {
 using caosdb::StatusCode;
-using caosdb::exceptions::AuthenticationError;
-using caosdb::exceptions::ConnectionError;
-using caosdb::exceptions::Exception;
 using caosdb::utility::get_arena;
 using google::protobuf::Arena;
 
@@ -47,7 +92,7 @@ bool UploadRequestHandler::OnNext(bool ok) {
   try {
     if (state_ == CallState::CallComplete) {
       this->handleCallCompleteState();
-      return false; // TODO(tf): comment
+      return false;
     } else if (ok) {
       if (state_ == CallState::NewCall) {
         this->handleNewCallState();
@@ -64,17 +109,20 @@ bool UploadRequestHandler::OnNext(bool ok) {
     }
 
     return true;
-  } catch (Exception &e) {
-    throw;
   } catch (std::exception &e) {
-    CAOSDB_LOG_ERROR(logger_name) << "Upload processing error: " << e.what();
+    CAOSDB_LOG_ERROR(logger_name)
+      << "UploadRequestHandler caught an exception: " << e.what();
+    transaction_status = TransactionStatus::GENERIC_ERROR(e.what());
+    state_ = CallState::CallComplete;
   } catch (...) {
     CAOSDB_LOG_ERROR(logger_name)
-      << "Upload processing error: unknown exception caught";
+      << "Transaction error: unknown exception caught";
+    transaction_status = TransactionStatus::GENERIC_ERROR(
+      "UploadRequestHandler caught an unknown exception");
+    state_ = CallState::CallComplete;
   }
 
   if (state_ == CallState::NewCall) {
-    // TODO(tf): comment
     return false;
   }
 
@@ -91,6 +139,7 @@ void UploadRequestHandler::handleNewCallState() {
 
   rpc_ = stub_->PrepareAsyncFileUpload(&ctx_, response_, cq_);
 
+  transaction_status = TransactionStatus::EXECUTING();
   state_ = CallState::SendingHeader;
   rpc_->StartCall(tag_);
 }
@@ -139,21 +188,31 @@ void UploadRequestHandler::handleExpectingResponseState() {
 }
 
 void UploadRequestHandler::handleCallCompleteState() {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter UploadRequestHandler::handleCallCompleteState";
+
   switch (status_.error_code()) {
   case grpc::OK: {
-    auto bytesSent = fileReader_ ? fileReader_->fileSize() : 0;
+    auto bytesSent = fileReader_ != nullptr ? fileReader_->fileSize() : 0;
     CAOSDB_LOG_INFO(logger_name)
-      << "[" << file_descriptor_.local_path
-      << "]: upload complete: " << bytesSent << " bytes sent" << std::endl;
+      << "UploadRequestHandler finished successfully ("
+      << file_descriptor_.local_path << "): upload complete, " << bytesSent
+      << " bytes sent";
+  } break;
+  default: {
+    auto code(static_cast<StatusCode>(status_.error_code()));
+    std::string description(get_status_description(code) +
+                            " Original message: " + status_.error_message());
+    transaction_status = TransactionStatus(code, description);
+    CAOSDB_LOG_ERROR(logger_name)
+      << "UploadRequestHandler finished with an error ("
+      << file_descriptor_.local_path << "): Upload aborted with code " << code
+      << " - " << description;
   } break;
-
-  case grpc::UNAUTHENTICATED:
-    throw AuthenticationError(status_.error_message());
-  case grpc::UNAVAILABLE:
-    throw ConnectionError(status_.error_message());
-  default:
-    throw Exception(StatusCode::GENERIC_RPC_ERROR, status_.error_message());
   }
+
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Leave UploadRequestHandler::handleCallCompleteState";
 }
 
 } // namespace caosdb::transaction
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index 214e0a76ff20fc11c7beb32c4461a6603ca811ba..2bb5b34f8ad0ec8a79b62c730c3d68ab50bdf188 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -20,11 +20,12 @@
 #include "caosdb/transaction.h"
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransac...
 #include "caosdb/entity/v1alpha1/main.pb.h"      // for TransactionRe...
-#include "caosdb/file_transmission/Client.h"     // for FileExchangeC...
+#include "caosdb/file_transmission/download_request_handler.h" // Download...
+#include "caosdb/file_transmission/file_reader.h"              // for path
 #include "caosdb/file_transmission/register_file_upload_handler.h"
-#include "caosdb/logging.h"         // for CAOSDB_LOG_FATAL
-#include "caosdb/protobuf_helper.h" // for get_arena
-#include "caosdb/status_code.h"     // for StatusCode
+#include "caosdb/file_transmission/upload_request_handler.h" // Upload...
+#include "caosdb/logging.h"     // for CAOSDB_LOG_FATAL
+#include "caosdb/status_code.h" // for StatusCode
 #include "caosdb/transaction_handler.h"
 #include <algorithm>                                   // for max
 #include <boost/filesystem/path.hpp>                   // for operator<<, path
@@ -285,7 +286,6 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
     CAOSDB_LOG_INFO(logger_name)
       << "Number of files to be uploaded: " << upload_files.size();
 
-    // TODO(tf): Use Arena
     auto *registration_request =
       Arena::CreateMessage<RegisterFileUploadRequest>(GetArena());
     auto *registration_response =
@@ -302,15 +302,15 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
       return StatusCode::EXECUTING;
     }
 
-    FileExchangeClient upload_client(file_service);
-    for (auto file_descriptor : upload_files) {
+    for (auto &file_descriptor : upload_files) {
       file_descriptor.file_transmission_id->set_registration_id(
         registration_response->registration_id());
       CAOSDB_LOG_INFO(logger_name)
         << "Uploading " << file_descriptor.local_path;
-      auto file_upload_status = upload_client.upload(file_descriptor);
-      if (file_upload_status != StatusCode::SUCCESS) {
-        this->status = TransactionStatus::FILE_UPLOAD_ERROR();
+      handler_ = std::make_unique<UploadRequestHandler>(
+        &handler_, file_service.get(), &completion_queue, file_descriptor);
+      this->status = ProcessCalls();
+      if (this->status.GetCode() != StatusCode::EXECUTING) {
         return StatusCode::EXECUTING;
       }
     }
@@ -319,12 +319,13 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
   CAOSDB_LOG_DEBUG(logger_name) << "RPC Request: " << RequestToString();
   handler_ = std::make_unique<EntityTransactionHandler>(
     &handler_, entity_service.get(), &completion_queue, request, response);
-
   this->status = ProcessCalls();
-  CAOSDB_LOG_DEBUG(logger_name) << "RPC Response: " << ResponseToString();
+  if (this->status.GetCode() != StatusCode::EXECUTING) {
+    return StatusCode::EXECUTING;
+  }
 
   // file download afterwards
-  if (status.GetCode() == StatusCode::SUCCESS && !download_files.empty()) {
+  if (status.GetCode() == StatusCode::EXECUTING && !download_files.empty()) {
     // run over all retrieved entities and get the download_id
     for (auto sub_response : *(response->mutable_responses())) {
       if (sub_response.transaction_response_case() ==
@@ -342,15 +343,15 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
       }
     }
 
-    FileExchangeClient download_client(file_service);
     for (const auto &item : download_files) {
       auto file_descriptor(item.second);
-      CAOSDB_DEBUG_MESSAGE_STRING(*file_descriptor.file_transmission_id, out)
       CAOSDB_LOG_INFO(logger_name)
-        << "Downloading " << file_descriptor.local_path << ", " << out;
-      auto file_download_status = download_client.download(file_descriptor);
-      if (file_download_status != StatusCode::SUCCESS) {
-        this->status = TransactionStatus::FILE_DOWNLOAD_ERROR();
+        << "Downloading " << file_descriptor.local_path;
+
+      handler_ = std::make_unique<DownloadRequestHandler>(
+        &handler_, file_service.get(), &completion_queue, file_descriptor);
+      this->status = ProcessCalls();
+      if (this->status.GetCode() != StatusCode::EXECUTING) {
         return StatusCode::EXECUTING;
       }
     }
@@ -359,6 +360,10 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
 }
 
 auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
+  if (this->status.GetCode() != StatusCode::EXECUTING) {
+    return this->status;
+  }
+  this->status = TransactionStatus::SUCCESS();
   bool set_error = false;
   auto *responses = this->response->mutable_responses();
   std::vector<std::unique_ptr<Entity>> entities;
diff --git a/src/caosdb/transaction_handler.cpp b/src/caosdb/transaction_handler.cpp
index 8e3f1388c47121da0b0dd6de49c00eb174251d89..cf020af35bf56b2e68da11a92b0961995397f2fd 100644
--- a/src/caosdb/transaction_handler.cpp
+++ b/src/caosdb/transaction_handler.cpp
@@ -37,8 +37,4 @@ void EntityTransactionHandler::handleNewCallState() {
     << "Leave EntityTransactionHandler::handleNewCallState";
 }
 
-void EntityTransactionHandler::handleReceivingFileState() {
-  // TODO(tf) remove
-}
-
 } // namespace caosdb::transaction
diff --git a/src/caosdb/unary_rpc_handler.cpp b/src/caosdb/unary_rpc_handler.cpp
index b41c13af66cff101fc91fa0ee1d283ffc0289483..4d1e1cf618ef119278b02fdc3657d17482a87601 100644
--- a/src/caosdb/unary_rpc_handler.cpp
+++ b/src/caosdb/unary_rpc_handler.cpp
@@ -1,3 +1,51 @@
+/*
+ * This file is a part of the CaosDB Project.
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *********************************************************************************
+ *
+ * This is derived work which is heavily based on
+ * https://github.com/NeiRo21/grpcpp-bidi-streaming, Commit
+ * cd9cb78e5d6d72806c2ec4c703e5e856b223dc96, Aug 10, 2020
+ *
+ * The orginal work is licensed as
+ *
+ * > MIT License
+ * >
+ * > Copyright (c) 2019 NeiRo21
+ * >
+ * > Permission is hereby granted, free of charge, to any person obtaining a
+ * > copy of this software and associated documentation files (the "Software"),
+ * > to deal in the Software without restriction, including without limitation
+ * > the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * > and/or sell copies of the Software, and to permit persons to whom the
+ * > Software is furnished to do so, subject to the following conditions:
+ * >
+ * > The above copyright notice and this permission notice shall be included in
+ * > all copies or substantial portions of the Software.
+ * >
+ * > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * > DEALINGS IN THE SOFTWARE.
+ */
 #include "caosdb/unary_rpc_handler.h"
 #include "caosdb/logging.h"                            // for CAOSDB_LOG_TRACE
 #include "caosdb/status_code.h"                        // for GENERIC_RPC_E...
@@ -21,8 +69,6 @@ bool UnaryRpcHandler::OnNext(bool ok) {
     if (ok) {
       if (state_ == CallState::NewCall) {
         this->handleNewCallState();
-      } else if (state_ == CallState::ReceivingFile) {
-        this->handleReceivingFileState();
       } else if (state_ == CallState::CallComplete) {
         this->handleCallCompleteState();
         return false;
@@ -69,7 +115,6 @@ void UnaryRpcHandler::handleCallCompleteState() {
 
   switch (status_.error_code()) {
   case grpc::OK:
-    transaction_status = TransactionStatus::SUCCESS();
     CAOSDB_LOG_INFO(logger_name) << "UnaryRpcHandler finished successfully.";
     break;
   default:
diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp
index c5afd8741d9803a9899a229c12d53f71a266317e..801b89db3a07f5b96e530cfbdfc8335c892a1331 100644
--- a/test/test_file_transmission.cpp
+++ b/test/test_file_transmission.cpp
@@ -1,5 +1,24 @@
-#include "caosdb/file_transmission/FileWriter.h"
-#include "caosdb/file_transmission/FileReader.h"
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "caosdb/file_transmission/file_writer.h"
+#include "caosdb/file_transmission/file_reader.h"
 #include <boost/filesystem/operations.hpp>  // for exists, file_size, remove
 #include <boost/filesystem/path.hpp>        // for path
 #include <boost/filesystem/path_traits.hpp> // for filesystem