diff --git a/nachrichten-manager-server/pom.xml b/nachrichten-manager-server/pom.xml
index 45fe94ccc1ecd26e3d33422d1e86d92603c77c79..28fe26cba3bca3d69eafc29654f923ce7e366430 100644
--- a/nachrichten-manager-server/pom.xml
+++ b/nachrichten-manager-server/pom.xml
@@ -49,6 +49,7 @@
 		<vorgang-manager.version>2.17.0-SNAPSHOT</vorgang-manager.version>
 		<muk-postfach.version>0.1.0-SNAPSHOT</muk-postfach.version>
 		<api-lib.version>0.13.0-SNAPSHOT</api-lib.version>
+		<ozgcloud-common.version>4.5.0-SNAPSHOT</ozgcloud-common.version>
 	</properties>
 
 	<dependencies>
@@ -100,6 +101,12 @@
 			<version>${api-lib.version}</version>
 		</dependency>
 
+		<dependency>
+			<groupId>de.ozgcloud.common</groupId>
+			<artifactId>ozgcloud-common-lib</artifactId>
+			<version>${ozgcloud-common.version}</version>
+		</dependency>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-mail</artifactId>
diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java
index 82dcc6d3e12f93a0f9b6e74c1a355e295215b058..3c1918cca9831e57400a8ed37c9b8374956505e7 100644
--- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java
+++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java
@@ -35,9 +35,9 @@ import org.springframework.core.task.TaskExecutor;
 import com.google.protobuf.ByteString;
 
 import de.ozgcloud.apilib.file.OzgCloudFile;
+import de.ozgcloud.common.binaryfile.GrpcBinaryFileServerDownloader;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.nachrichten.NachrichtenManagerConfiguration;
-import de.ozgcloud.nachrichten.common.grpc.GrpcDownloader;
 import de.ozgcloud.nachrichten.common.vorgang.VorgangService;
 import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
 import io.grpc.stub.CallStreamObserver;
@@ -134,9 +134,9 @@ class AntragraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplB
 		buildAttachmentDownloader(attachmentFileRequestMapper.fromContentRequest(request), responseObserver).start();
 	}
 
-	GrpcDownloader<GrpcGetAttachmentContentResponse> buildAttachmentDownloader(AttachmentFileRequest request,
+	GrpcBinaryFileServerDownloader<GrpcGetAttachmentContentResponse> buildAttachmentDownloader(AttachmentFileRequest request,
 			StreamObserver<GrpcGetAttachmentContentResponse> responseObserver) {
-		return GrpcDownloader.<GrpcGetAttachmentContentResponse>builder()
+		return GrpcBinaryFileServerDownloader.<GrpcGetAttachmentContentResponse>builder()
 				.callObserver((CallStreamObserver<GrpcGetAttachmentContentResponse>) responseObserver)
 				.taskExecutor(taskExecutor)
 				.downloadConsumer(outputStream -> service.getAttachmentContent(request, outputStream))
diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/ExceptionRunnable.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/ExceptionRunnable.java
deleted file mode 100644
index 0c22c7faf853d68c011fee0c5e89007ac19f97a8..0000000000000000000000000000000000000000
--- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/ExceptionRunnable.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.ozgcloud.nachrichten.common.grpc;
-
-import java.io.IOException;
-
-interface ExceptionRunnable {
-	void run() throws IOException; // NOSONAR
-}
\ No newline at end of file
diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloader.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloader.java
deleted file mode 100644
index 7f34bd46bda9e0334641416c31751bc9b5d13871..0000000000000000000000000000000000000000
--- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloader.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package de.ozgcloud.nachrichten.common.grpc;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import org.springframework.core.task.TaskExecutor;
-
-import com.google.protobuf.ByteString;
-
-import de.ozgcloud.common.errorhandling.TechnicalException;
-import io.grpc.stub.CallStreamObserver;
-import lombok.Builder;
-
-public class GrpcDownloader<T> {
-
-	private static final int CHUNK_SIZE = 255 * 1024;
-
-	private CallStreamObserver<T> callObserver;
-	private Function<ByteString, T> chunkBuilder;
-	private Consumer<OutputStream> downloadConsumer;
-	private TaskExecutor taskExecutor;
-
-	private final byte[] buffer = new byte[GrpcDownloader.CHUNK_SIZE];
-	private final AtomicBoolean downloadInProgress = new AtomicBoolean(false);
-
-	private PipedInputStream inputStream;
-	private PipedOutputStream outputStream;
-
-	@Builder
-	public GrpcDownloader(CallStreamObserver<T> callObserver, Function<ByteString, T> chunkBuilder,
-			Consumer<OutputStream> downloadConsumer, TaskExecutor taskExecutor) {
-		this.callObserver = callObserver;
-		this.chunkBuilder = chunkBuilder;
-		this.downloadConsumer = downloadConsumer;
-		this.taskExecutor = taskExecutor;
-	}
-
-	public void start() {
-		handleSafety(this::doStart);
-	}
-
-	void doStart() throws IOException {
-		setupStreams();
-		taskExecutor.execute(this::startDownload);
-		callObserver.setOnReadyHandler(this::onReadyHandler);
-	}
-
-	void setupStreams() throws IOException {
-		outputStream = new PipedOutputStream();
-		inputStream = new PipedInputStream(GrpcDownloader.CHUNK_SIZE);
-		outputStream.connect(inputStream);
-	}
-
-	void startDownload() {
-		handleSafety(this::doDownload);
-	}
-
-	void doDownload() throws IOException {
-		downloadInProgress.set(true);
-		downloadConsumer.accept(outputStream);
-		downloadInProgress.set(false);
-		outputStream.close();
-	}
-
-	synchronized void onReadyHandler() {
-		if (callObserver.isReady()) {
-			sendChunks();
-		}
-	}
-
-	void sendChunks() {
-		handleSafety(this::doSendChunks);
-	}
-
-	void doSendChunks() throws IOException {
-		int bytesRead;
-		while (callObserver.isReady() && (bytesRead = inputStream.read(buffer)) != -1) {
-			callObserver.onNext(chunkBuilder.apply(ByteString.copyFrom(buffer, 0, bytesRead)));
-		}
-		if (!downloadInProgress.get()) {
-			inputStream.close();
-			callObserver.onCompleted();
-		}
-	}
-
-	void handleSafety(ExceptionRunnable runnable) {
-		try {
-			runnable.run();
-		} catch (IOException e) {
-			try {
-				inputStream.close();
-				outputStream.close();
-			} catch (IOException e1) {
-				throwException(e1);
-			}
-			throwException(e);
-		}
-	}
-
-	private void throwException(IOException e) {
-		throw new TechnicalException("Error occurred during downloading file content download.", e);
-	}
-}
\ No newline at end of file
diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java
index 8584a2d117dd7a7aa0c47ca6c54910823bacc089..cde69a9d74df8dc17d6fa5aedf4859282c59e717 100644
--- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java
+++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java
@@ -52,8 +52,8 @@ import com.thedeanda.lorem.LoremIpsum;
 
 import de.ozgcloud.apilib.file.OzgCloudFile;
 import de.ozgcloud.apilib.file.OzgCloudFileTestFactory;
+import de.ozgcloud.common.binaryfile.GrpcBinaryFileServerDownloader;
 import de.ozgcloud.common.errorhandling.TechnicalException;
-import de.ozgcloud.nachrichten.common.grpc.GrpcDownloader;
 import de.ozgcloud.nachrichten.common.vorgang.GrpcServiceKontoTestFactory;
 import de.ozgcloud.nachrichten.common.vorgang.Vorgang;
 import de.ozgcloud.nachrichten.common.vorgang.VorgangService;
@@ -473,7 +473,7 @@ class AntragraumGrpcServiceTest {
 		@Mock
 		private StreamObserver<GrpcGetAttachmentContentResponse> responseObserver;
 		@Mock
-		private GrpcDownloader<GrpcGetAttachmentContentResponse> downloader;
+		private GrpcBinaryFileServerDownloader<GrpcGetAttachmentContentResponse> downloader;
 
 		private final GrpcGetAttachmentContentRequest grpcRequest = GrpcGetAttachmentContentRequestTestFactory.create();
 		private final AttachmentFileRequest request = AttachmentFileRequestTestFactory.create();
@@ -517,8 +517,8 @@ class AntragraumGrpcServiceTest {
 		@Mock
 		private OutputStream outputStream;
 
-		private MockedConstruction<GrpcDownloader> downloaderMockedConstruction;
-		private GrpcDownloader<GrpcGetAttachmentContentResponse> downloader;
+		private MockedConstruction<GrpcBinaryFileServerDownloader> downloaderMockedConstruction;
+		private GrpcBinaryFileServerDownloader<GrpcGetAttachmentContentResponse> downloader;
 		private StreamObserver<GrpcGetAttachmentContentResponse> setResponseObserver;
 		private TaskExecutor setTaskExecutor;
 		private Consumer<OutputStream> setDownloadConsumer;
@@ -530,7 +530,7 @@ class AntragraumGrpcServiceTest {
 		@SuppressWarnings("unchecked")
 		@BeforeEach
 		void mock() {
-			downloaderMockedConstruction = mockConstruction(GrpcDownloader.class, (downloader, context) -> {
+			downloaderMockedConstruction = mockConstruction(GrpcBinaryFileServerDownloader.class, (downloader, context) -> {
 				setResponseObserver = (StreamObserver<GrpcGetAttachmentContentResponse>) context.arguments().get(0);
 				setChunkBuilder = (Function<ByteString, GrpcGetAttachmentContentResponse>) context.arguments().get(1);
 				setDownloadConsumer = (Consumer<OutputStream>) context.arguments().get(2);
@@ -585,7 +585,7 @@ class AntragraumGrpcServiceTest {
 			assertThat(returnedDownloader).isEqualTo(downloader);
 		}
 
-		private GrpcDownloader<GrpcGetAttachmentContentResponse> callBuildAttachmentDownloader() {
+		private GrpcBinaryFileServerDownloader<GrpcGetAttachmentContentResponse> callBuildAttachmentDownloader() {
 			return grpcService.buildAttachmentDownloader(request, responseObserver);
 		}
 	}
diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloaderTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloaderTest.java
deleted file mode 100644
index 4f7c0c5b85abe8af0b8b9a958a8392ea738b2591..0000000000000000000000000000000000000000
--- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/common/grpc/GrpcDownloaderTest.java
+++ /dev/null
@@ -1,330 +0,0 @@
-package de.ozgcloud.nachrichten.common.grpc;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.Spy;
-import org.springframework.core.task.TaskExecutor;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import com.google.protobuf.ByteString;
-
-import de.ozgcloud.common.errorhandling.TechnicalException;
-import io.grpc.stub.ClientCallStreamObserver;
-import lombok.SneakyThrows;
-
-class GrpcDownloaderTest {
-
-	@SuppressWarnings("unchecked")
-	private ClientCallStreamObserver<GrpcResponseDummy> callObserver = Mockito.mock(ClientCallStreamObserver.class);
-	@SuppressWarnings("unchecked")
-	private Function<ByteString, GrpcResponseDummy> chunkBuilder = Mockito.mock(Function.class);
-	@SuppressWarnings("unchecked")
-	private Consumer<OutputStream> downloadConsumer = Mockito.mock(Consumer.class);
-	private TaskExecutor taskExecutor = mock(TaskExecutor.class);
-
-	@Spy
-	private GrpcDownloader<GrpcResponseDummy> downloader = GrpcDownloader.<GrpcResponseDummy>builder()
-			.callObserver(callObserver)
-			.downloadConsumer(downloadConsumer)
-			.chunkBuilder(chunkBuilder)
-			.taskExecutor(taskExecutor)
-			.build();
-
-	@DisplayName("Start")
-	@Nested
-	class TestStart {
-
-		@SneakyThrows
-		@Test
-		void shouldCallDoStart() {
-			doNothing().when(downloader).doStart();
-
-			downloader.start();
-
-			verify(downloader).doStart();
-		}
-
-		@SneakyThrows
-		@Test
-		void shouldCallHandleSafety() {
-			doNothing().when(downloader).doStart();
-
-			downloader.start();
-
-			verify(downloader).handleSafety(any(ExceptionRunnable.class));
-		}
-
-		@DisplayName("do")
-		@Nested
-		class TestDoStart {
-
-			@Captor
-			private ArgumentCaptor<Runnable> runnableCaptor;
-
-			@SneakyThrows
-			@Test
-			void shouldSetupStreams() {
-				downloader.doStart();
-
-				verify(downloader).setupStreams();
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCallTaskExecutor() {
-				downloader.doStart();
-
-				verify(taskExecutor).execute(any());
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldStartDownload() {
-				doNothing().when(downloader).startDownload();
-
-				downloader.doStart();
-
-				verify(taskExecutor).execute(runnableCaptor.capture());
-				runnableCaptor.getValue().run();
-				verify(downloader).startDownload();
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldSetOnReadyHandler() {
-				downloader.doStart();
-
-				verify(callObserver).setOnReadyHandler(runnableCaptor.capture());
-				assertThat(runnableCaptor.getValue()).isNotNull();
-			}
-		}
-	}
-
-	@DisplayName("Start download")
-	@Nested
-	class TestStartDownload {
-
-		@SneakyThrows
-		@Test
-		void shouldCallHandleSafety() {
-			doNothing().when(downloader).doDownload();
-
-			downloader.startDownload();
-
-			verify(downloader).handleSafety(any(ExceptionRunnable.class));
-		}
-
-		@SneakyThrows
-		@Test
-		void shouldCallDoDownoad() {
-			doNothing().when(downloader).doDownload();
-
-			downloader.startDownload();
-
-			verify(downloader).doDownload();
-		}
-
-		@DisplayName("do")
-		@Nested
-		class TestDoDownload {
-
-			@Mock
-			private PipedOutputStream outputStream;
-
-			@BeforeEach
-			void mock() {
-				ReflectionTestUtils.setField(downloader, "outputStream", outputStream);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCallDownloadConsumer() {
-				downloader.doDownload();
-
-				verify(downloadConsumer).accept(outputStream);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCloseOutputstream() {
-				downloader.doDownload();
-
-				verify(outputStream).close();
-			}
-		}
-	}
-
-	@DisplayName("On ready handler")
-	@Nested
-	class TestOnReadyHandler {
-
-		@Test
-		void shouldSendChunksIfCallObserverIsReady() {
-			when(callObserver.isReady()).thenReturn(true);
-			doNothing().when(downloader).sendChunks();
-
-			downloader.onReadyHandler();
-
-			verify(downloader).sendChunks();
-		}
-	}
-
-	@DisplayName("Send chunks")
-	@Nested
-	class TestSendChunks {
-
-		@SneakyThrows
-		@Test
-		void shouldCallHandleSafety() {
-			doNothing().when(downloader).doSendChunks();
-
-			downloader.sendChunks();
-
-			verify(downloader).handleSafety(any(ExceptionRunnable.class));
-		}
-
-		@SneakyThrows
-		@Test
-		void shouldCallDoDownoad() {
-			doNothing().when(downloader).doSendChunks();
-
-			downloader.sendChunks();
-
-			verify(downloader).doSendChunks();
-		}
-
-		@DisplayName("do")
-		@Nested
-		class TestDoSendChunks {
-
-			@Mock
-			private PipedInputStream inputStream;
-
-			private final int data = 20;
-
-			@BeforeEach
-			void mock() {
-				ReflectionTestUtils.setField(downloader, "inputStream", inputStream);
-			}
-
-			@SneakyThrows
-			@DisplayName("should send next chunk if callObserver is ready and stream already received data")
-			@Test
-			void shouldCallOnNext() {
-				when(callObserver.isReady()).thenReturn(true);
-				when(inputStream.read(any())).thenReturn(data, -1);
-
-				downloader.doSendChunks();
-
-				verify(callObserver).onNext(any());
-			}
-
-			@SneakyThrows
-			@DisplayName("should call complete grpc stream if download has finished and stream has no data left")
-			@Test
-			void shouldCallOnCompleted() {
-				ReflectionTestUtils.setField(downloader, "downloadInProgress", new AtomicBoolean(false));
-
-				downloader.doSendChunks();
-
-				verify(callObserver).onCompleted();
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCloseInputStream() {
-				ReflectionTestUtils.setField(downloader, "downloadInProgress", new AtomicBoolean(false));
-
-				downloader.doSendChunks();
-
-				verify(inputStream).close();
-			}
-		}
-	}
-
-	@DisplayName("Handle safety")
-	@Nested
-	class TestHandleSafety {
-
-		@DisplayName("on exception")
-		@Nested
-		class TestOnException {
-
-			@Mock
-			private PipedOutputStream outputStream;
-			@Mock
-			private PipedInputStream inputStream;
-
-			private final IOException exception = new IOException();
-
-			@SneakyThrows
-			@BeforeEach
-			void mock() {
-				ReflectionTestUtils.setField(downloader, "inputStream", inputStream);
-				ReflectionTestUtils.setField(downloader, "outputStream", outputStream);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldThrowTechnicalException() {
-				assertThatThrownBy(this::handleSafety)
-						.isInstanceOf(TechnicalException.class)
-						.extracting(exception -> exception.getCause()).isEqualTo(exception);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCloseOutputStream() {
-				try {
-					handleSafety();
-				} catch (Exception e) {
-					// do nothing
-				}
-
-				verify(outputStream).close();
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCloseInputStream() {
-				try {
-					handleSafety();
-				} catch (Exception e) {
-					// do nothing
-				}
-
-				verify(inputStream).close();
-			}
-
-			private void handleSafety() {
-				downloader.handleSafety(this::dummyMethodThrowingException);
-			}
-
-			private void dummyMethodThrowingException() throws IOException {
-				throw exception;
-			}
-		}
-
-	}
-
-	class GrpcResponseDummy {
-	}
-}
\ No newline at end of file