diff --git a/README.md b/README.md
index 7da81c140920962b6df6faceba2a4ab4a5062f4a..e1c42f10379ac38ed06dfa23b7807eb84ff0d84a 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ The allows to zip a folder and/or upload a whole stream in chunks to a S3 object
 
 For example:
 ```
-var s3 = new S3Zip(s3MultipartStreamConfiguration);
-s3.ZipAndUploadFolder(@"c:\temp\test", "Test.zip");
+var zipUtilities = new ZipUtilities(_s3MultipartStreamConfiguration);
+zipUtilities.ZipAndUploadFolder(@"c:\temp\test", "test.zip");
 ```
 
-A zip file of the folder will be in S3 under the key Test.zip.
\ No newline at end of file
+A zip file of the folder will be in S3 under the key test.zip.
\ No newline at end of file
diff --git a/docs/home.md b/docs/home.md
index 7da81c140920962b6df6faceba2a4ab4a5062f4a..e1c42f10379ac38ed06dfa23b7807eb84ff0d84a 100644
--- a/docs/home.md
+++ b/docs/home.md
@@ -4,8 +4,8 @@ The allows to zip a folder and/or upload a whole stream in chunks to a S3 object
 
 For example:
 ```
-var s3 = new S3Zip(s3MultipartStreamConfiguration);
-s3.ZipAndUploadFolder(@"c:\temp\test", "Test.zip");
+var zipUtilities = new ZipUtilities(_s3MultipartStreamConfiguration);
+zipUtilities.ZipAndUploadFolder(@"c:\temp\test", "test.zip");
 ```
 
-A zip file of the folder will be in S3 under the key Test.zip.
\ No newline at end of file
+A zip file of the folder will be in S3 under the key test.zip.
\ No newline at end of file
diff --git a/src/cs-S3Zip.Tests/Properties/AssemblyInfo.cs b/src/cs-S3Zip.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e6da98f0c71e9aa38cdecd05afd8c134120ffa1e
--- /dev/null
+++ b/src/cs-S3Zip.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("cs-S3Zip.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("cs-S3Zip.Tests")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6b58eb5e-d857-464b-8174-c17375bae1cf")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/cs-S3Zip.Tests/S3ZipTests.cs b/src/cs-S3Zip.Tests/S3ZipTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df276e11a1333dfce1d0607b2210e7ab334b962c
--- /dev/null
+++ b/src/cs-S3Zip.Tests/S3ZipTests.cs
@@ -0,0 +1,107 @@
+
+using Amazon.S3;
+using Amazon.S3.Model;
+using NUnit.Framework;
+using System;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace coscine.cs_S3Zip.Tests
+{
+    [TestFixture]
+    public class S3ZipTests
+    {
+        private readonly S3MultipartStreamConfiguration _s3MultipartStreamConfiguration;
+
+        private readonly int ONE_MB;
+
+        public S3ZipTests()
+        {
+            var accessKey = Environment.GetEnvironmentVariable("S3_ACCESS_KEY", EnvironmentVariableTarget.Process);
+            var secretKey = Environment.GetEnvironmentVariable("S3_SECRET_KEY", EnvironmentVariableTarget.Process);
+
+            ONE_MB = (int)Math.Pow(2, 20);
+
+            _s3MultipartStreamConfiguration = new S3MultipartStreamConfiguration
+            {
+                AccessKey = accessKey,
+                SecretKey = secretKey,
+                BucketName = "PITLABTTEST",
+                AmazonS3Config = new AmazonS3Config
+                {
+                    ServiceURL = "https://s3.rwth-aachen.de/",
+                    ForcePathStyle = true
+                }
+            };
+        }
+
+        [TestCase(110, 50)]
+        [TestCase(110, 5)]
+        [TestCase(30, 50)]
+        [TestCase(30, 5)]
+        public void S3StreamTest(int length, int chunkSize)
+        {
+            var key = "S3StreamTest.txt";
+            var chars = "$%#@!*abcdefghijklmnopqrstuvwxyz1234567890?;:ABCDEFGHIJKLMNOPQRSTUVWXYZ^&".ToCharArray();
+            var seed = DateTime.Now.Millisecond;
+            var random = new Random(seed);
+            var randomBuffer = new byte[ONE_MB];
+
+            var oldChunkSize = _s3MultipartStreamConfiguration.ChunckSize;
+            _s3MultipartStreamConfiguration.ChunckSize = chunkSize * ONE_MB;
+
+            using (var s3Stream = new S3MultipartStream(key, _s3MultipartStreamConfiguration))
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    FillArrayWithRandomChars(randomBuffer, randomBuffer.Length, chars, random);
+                    s3Stream.Write(randomBuffer, 0, ONE_MB);
+                }
+            }
+
+            var request = new GetObjectRequest
+            {
+                BucketName = _s3MultipartStreamConfiguration.BucketName,
+                Key = key
+            };
+
+            // reset the random number generator
+            random = new Random(seed);
+
+            using (var client = new AmazonS3Client(_s3MultipartStreamConfiguration.AccessKey, _s3MultipartStreamConfiguration.SecretKey, _s3MultipartStreamConfiguration.AmazonS3Config))
+            {
+                var buffer = new byte[ONE_MB];
+                using (var response = client.GetObject(request))
+                {
+                    // Was request successfull?
+                    Assert.IsTrue(response.HttpStatusCode == System.Net.HttpStatusCode.OK);
+                    // Has the correct length?
+                    Assert.IsTrue(response.ContentLength == length * ONE_MB);
+
+                    using (var responseStream = response.ResponseStream)
+                    {
+                        int alreadyRead = 0;
+                        for (int i = 0; i < length; i++)
+                        {
+                            int read = responseStream.Read(buffer, 0, buffer.Length);
+                            FillArrayWithRandomChars(randomBuffer, read, chars, random);
+                            // check if the data is equal to the previously generated one.
+                            Assert.IsTrue(Enumerable.SequenceEqual(randomBuffer.Take(read), buffer.Take(read)));
+                            alreadyRead += read;
+                        }
+                    }
+                }
+            }
+            _s3MultipartStreamConfiguration.ChunckSize = oldChunkSize;
+        }
+
+        private void FillArrayWithRandomChars(byte[] randomBuffer, int length, char[] chars, Random random)
+        {
+            for (int i = 0; i < length; i++)
+            {
+                randomBuffer[i] = (byte)chars[random.Next(chars.Length)];
+            }
+        }
+    }
+}
diff --git a/src/cs-S3Zip.Tests/cs-S3Zip.Tests.csproj b/src/cs-S3Zip.Tests/cs-S3Zip.Tests.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..2cfe828b83a8aae8cc8c24ac945b09aa16437566
--- /dev/null
+++ b/src/cs-S3Zip.Tests/cs-S3Zip.Tests.csproj
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" />
+  <Import Project="..\packages\NUnit.3.11.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.11.0\build\NUnit.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{6B58EB5E-D857-464B-8174-C17375BAE1CF}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>coscine.cs_S3Zip.Tests</RootNamespace>
+    <AssemblyName>cs-S3Zip.Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="AWSSDK.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604" />
+    <Reference Include="AWSSDK.S3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604" />
+    <Reference Include="nunit.framework, Version=3.11.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
+      <HintPath>..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="S3ZipTests.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\cs-S3Zip\cs-S3Zip.csproj">
+      <Project>{7AC0CBDF-046B-496E-931B-776678098C7E}</Project>
+      <Name>cs-S3Zip</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\NUnit.3.11.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.11.0\build\NUnit.props'))" />
+    <Error Condition="!Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props'))" />
+  </Target>
+</Project>
\ No newline at end of file
diff --git a/src/cs-S3Zip.Tests/packages.config b/src/cs-S3Zip.Tests/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..cde7f3d9f886b890b8e3765997d986994e6497cb
--- /dev/null
+++ b/src/cs-S3Zip.Tests/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NUnit" version="3.11.0" targetFramework="net472" />
+  <package id="NUnit3TestAdapter" version="3.13.0" targetFramework="net472" />
+</packages>
\ No newline at end of file
diff --git a/src/cs-S3Zip.sln b/src/cs-S3Zip.sln
new file mode 100644
index 0000000000000000000000000000000000000000..21c63b117459395eb48c0c37418b9d10df00fd64
--- /dev/null
+++ b/src/cs-S3Zip.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28803.156
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs-S3Zip", "cs-S3Zip\cs-S3Zip.csproj", "{7AC0CBDF-046B-496E-931B-776678098C7E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs-S3Zip.Tests", "cs-S3Zip.Tests\cs-S3Zip.Tests.csproj", "{6B58EB5E-D857-464B-8174-C17375BAE1CF}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{7AC0CBDF-046B-496E-931B-776678098C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7AC0CBDF-046B-496E-931B-776678098C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7AC0CBDF-046B-496E-931B-776678098C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7AC0CBDF-046B-496E-931B-776678098C7E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6B58EB5E-D857-464B-8174-C17375BAE1CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6B58EB5E-D857-464B-8174-C17375BAE1CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6B58EB5E-D857-464B-8174-C17375BAE1CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6B58EB5E-D857-464B-8174-C17375BAE1CF}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {CB4D8BFF-53DB-45F3-9A1B-8572F8A2D443}
+	EndGlobalSection
+EndGlobal
diff --git a/src/cs-S3Zip/Properties/AssemblyInfo.cs b/src/cs-S3Zip/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..74d2e66ba90e3bc1726c003bcdc711e847fdf912
--- /dev/null
+++ b/src/cs-S3Zip/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("cs-S3Zip")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("cs-S3Zip")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("7ac0cbdf-046b-496e-931b-776678098c7e")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/cs-S3Zip/S3MultipartStream.cs b/src/cs-S3Zip/S3MultipartStream.cs
new file mode 100644
index 0000000000000000000000000000000000000000..90f378ceadc59a71bede44bfc99c8458acb91458
--- /dev/null
+++ b/src/cs-S3Zip/S3MultipartStream.cs
@@ -0,0 +1,158 @@
+using Amazon.S3;
+using Amazon.S3.Model;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace coscine.cs_S3Zip
+{
+    public class S3MultipartStream : Stream
+    {
+        private readonly Stream _internalStream;
+        private readonly InitiateMultipartUploadResponse _initiateMultipartUploadResponse;
+        private readonly List<UploadPartResponse> _uploadPartResponses;
+        private int _part;
+        private int _length;
+        private int _position;
+
+        public S3MultipartStream(string key, S3MultipartStreamConfiguration s3MultipartStreamConfiguration, Stream internalStream)
+        {
+            S3MultipartStreamConfiguration = s3MultipartStreamConfiguration;
+
+            _internalStream = internalStream;
+            _uploadPartResponses = new List<UploadPartResponse>();
+            _part = 0;
+            _length = 0;
+            _position = 0;
+
+            Key = key;
+
+            var initiateRequest = new InitiateMultipartUploadRequest
+            {
+                BucketName = S3MultipartStreamConfiguration.BucketName,
+                Key = key
+            };
+
+            using (var client = new AmazonS3Client(S3MultipartStreamConfiguration.AccessKey, S3MultipartStreamConfiguration.SecretKey, S3MultipartStreamConfiguration.AmazonS3Config))
+            {
+                _initiateMultipartUploadResponse = client.InitiateMultipartUpload(initiateRequest);
+            }
+        }
+
+        public S3MultipartStream(string key, S3MultipartStreamConfiguration s3MultipartStreamConfiguration) : this(key, s3MultipartStreamConfiguration, new MemoryStream(s3MultipartStreamConfiguration.ChunckSize))
+        {
+
+        }
+
+        public string Key { get; }
+
+        public S3MultipartStreamConfiguration S3MultipartStreamConfiguration { get; set; }
+
+        public override bool CanRead => false;
+
+        public override bool CanSeek => false;
+
+        public override bool CanWrite => true;
+
+        public override long Length => _length;
+
+        public override long Position { get => _position; set => throw new NotImplementedException(); }
+
+        public override void Flush()
+        {
+            _internalStream.Flush();
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void SetLength(long value)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            int written = 0;
+            while (written < count)
+            {
+                if (count - written + _internalStream.Length <= S3MultipartStreamConfiguration.ChunckSize)
+                {
+                    _internalStream.Write(buffer, offset + written, count - written);
+                    break;
+                }
+                else
+                {
+                    int toBeWritten = S3MultipartStreamConfiguration.ChunckSize - (int)_internalStream.Length;
+                    _internalStream.Write(buffer, offset + written, toBeWritten);
+                    written += toBeWritten;
+                    UploadPart();
+
+                    // resets stream
+                    _internalStream.SetLength(0);
+                }
+            }
+
+            _length += count;
+            _position += count;
+        }
+
+        private void UploadPart()
+        {
+            using (var client = new AmazonS3Client(S3MultipartStreamConfiguration.AccessKey, S3MultipartStreamConfiguration.SecretKey, S3MultipartStreamConfiguration.AmazonS3Config))
+            {
+                _internalStream.Position = 0;
+
+                var uploadPartRequest = new UploadPartRequest
+                {
+                    BucketName = S3MultipartStreamConfiguration.BucketName,
+                    Key = Key,
+                    UploadId = _initiateMultipartUploadResponse.UploadId,
+                    PartNumber = _part + 1,
+                    PartSize = _internalStream.Length,
+                    InputStream = _internalStream
+                };
+
+                _uploadPartResponses.Add(client.UploadPart(uploadPartRequest));
+                _part++;
+            }
+        }
+
+        private void FinalizeS3MultipartUpload()
+        {
+            var completeRequest = new CompleteMultipartUploadRequest
+            {
+                BucketName = S3MultipartStreamConfiguration.BucketName,
+                Key = Key,
+                UploadId = _initiateMultipartUploadResponse.UploadId
+            };
+
+            completeRequest.AddPartETags(_uploadPartResponses);
+
+            using (var client = new AmazonS3Client(S3MultipartStreamConfiguration.AccessKey, S3MultipartStreamConfiguration.SecretKey, S3MultipartStreamConfiguration.AmazonS3Config))
+            {
+                client.CompleteMultipartUpload(completeRequest);
+            }
+        }
+
+        // Finalize on stream close clall.
+        public override void Close()
+        {
+            if(_internalStream.Length > 0)
+            {
+                UploadPart();
+            }
+
+            FinalizeS3MultipartUpload();
+
+            _internalStream.Close();
+        }
+    }
+}
diff --git a/src/cs-S3Zip/S3MultipartStreamConfiguration.cs b/src/cs-S3Zip/S3MultipartStreamConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8c1119f52e588b4a73fbc147c2c8e1beeb824436
--- /dev/null
+++ b/src/cs-S3Zip/S3MultipartStreamConfiguration.cs
@@ -0,0 +1,18 @@
+using Amazon.S3;
+using System;
+
+namespace coscine.cs_S3Zip
+{
+    public class S3MultipartStreamConfiguration
+    {
+        public string AccessKey { get; set; }
+
+        public string SecretKey { get; set; }
+
+        public string BucketName { get; set; }
+
+        public AmazonS3Config AmazonS3Config { get; set; } 
+
+        public int ChunckSize { get; set; } = 50 * (int)Math.Pow(2, 20);
+    }
+}
diff --git a/src/cs-S3Zip/ZipUtilities.cs b/src/cs-S3Zip/ZipUtilities.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b717ce7c32c110b2c8e159f4f5de12cf3c7876ee
--- /dev/null
+++ b/src/cs-S3Zip/ZipUtilities.cs
@@ -0,0 +1,75 @@
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+
+namespace coscine.cs_S3Zip
+{
+    public class ZipUtilities
+    {
+        public S3MultipartStreamConfiguration S3ZipConfiguration { get; set; }
+
+        public ZipUtilities(S3MultipartStreamConfiguration s3ZipConfiguration)
+        {
+            S3ZipConfiguration = s3ZipConfiguration;
+        }
+
+        public void ZipFolder(string path, ZipArchive zipArchive)
+        {
+            ZipFolder(new DirectoryInfo(path), zipArchive);
+        }
+
+        public void ZipFolder(DirectoryInfo directoryInfo, ZipArchive zipArchive)
+        {
+            var prefixLength = directoryInfo.FullName.EndsWith(@"\") ? directoryInfo.FullName.Length : directoryInfo.FullName.Length + 1;
+            ZipFiles(directoryInfo.GetFiles("*", SearchOption.AllDirectories), prefixLength, zipArchive);
+        }
+
+        public void ZipFiles(IEnumerable<FileInfo> files, int prefixLength, ZipArchive zipArchive)
+        {
+            foreach (var file in files)
+            {
+                // cut off parent directory path
+                var fileEntry = zipArchive.CreateEntry(file.FullName.Substring(prefixLength));
+                using (var fileStream = file.OpenRead())
+                {
+                    ZipStream(fileStream, fileEntry);
+                }
+            }
+        }
+
+        public void ZipStream(Stream stream, ZipArchiveEntry fileEntry)
+        {
+            using (var entryStream = fileEntry.Open())
+            {
+                stream.CopyTo(entryStream);
+            }
+        }
+
+
+        public void ZipAndUploadFolder(string path)
+        {
+            ZipAndUploadFolder(new DirectoryInfo(path));
+        }
+
+        public void ZipAndUploadFolder(DirectoryInfo directoryInfo)
+        {
+            ZipAndUploadFolder(directoryInfo, directoryInfo.Name);
+        }
+
+        public void ZipAndUploadFolder(string path, string key)
+        {
+            ZipAndUploadFolder(new DirectoryInfo(path), key);
+        }
+
+        public void ZipAndUploadFolder(DirectoryInfo directoryInfo, string key)
+        {
+            using (var s3Stream = new S3MultipartStream(key, S3ZipConfiguration))
+            {
+                using (var archive = new ZipArchive(s3Stream, ZipArchiveMode.Create, true))
+                {
+                    ZipFolder(directoryInfo, archive);
+                }
+            }
+        }
+    }
+}
diff --git a/src/cs-S3Zip/cs-S3Zip.csproj b/src/cs-S3Zip/cs-S3Zip.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..49c61bf658d90b6c90332cf6b346e9fb545f1cc6
--- /dev/null
+++ b/src/cs-S3Zip/cs-S3Zip.csproj
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{7AC0CBDF-046B-496E-931B-776678098C7E}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>coscine.cs_S3Zip</RootNamespace>
+    <AssemblyName>cs-S3Zip</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="AWSSDK.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
+      <HintPath>..\packages\AWSSDK.Core.3.3.101.1\lib\net45\AWSSDK.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="AWSSDK.S3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
+      <HintPath>..\packages\AWSSDK.S3.3.3.101.8\lib\net45\AWSSDK.S3.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.IO.Compression" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="S3MultipartStream.cs" />
+    <Compile Include="ZipUtilities.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="S3MultipartStreamConfiguration.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Analyzer Include="..\packages\AWSSDK.S3.3.3.101.8\analyzers\dotnet\cs\AWSSDK.S3.CodeAnalysis.dll" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/src/cs-S3Zip/packages.config b/src/cs-S3Zip/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..ee7c9340b5b6d011342e80f5d0d1dce5526425f3
--- /dev/null
+++ b/src/cs-S3Zip/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="AWSSDK.Core" version="3.3.101.1" targetFramework="net472" />
+  <package id="AWSSDK.S3" version="3.3.101.8" targetFramework="net472" />
+</packages>
\ No newline at end of file