<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hooman Bahraini</title>
    <description>The latest articles on DEV Community by Hooman Bahraini (@hoomanbahreini).</description>
    <link>https://dev.to/hoomanbahreini</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F287362%2Ff4077556-1167-4c3d-905a-8a605ebd3aa4.jpeg</url>
      <title>DEV Community: Hooman Bahraini</title>
      <link>https://dev.to/hoomanbahreini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hoomanbahreini"/>
    <language>en</language>
    <item>
      <title>How to use half and quarter  columns using Bootsrap 4 grid system</title>
      <dc:creator>Hooman Bahraini</dc:creator>
      <pubDate>Sun, 21 Mar 2021 02:17:45 +0000</pubDate>
      <link>https://dev.to/hoomanbahreini/how-to-use-half-and-quarter-columns-using-bootsrap-4-grid-system-3e3p</link>
      <guid>https://dev.to/hoomanbahreini/how-to-use-half-and-quarter-columns-using-bootsrap-4-grid-system-3e3p</guid>
      <description>&lt;p&gt;We know that Bootstrap bootstrap divides the screen into 12 columns. Each column is 1/12th of entire screen... &lt;/p&gt;

&lt;p&gt;But what if you want to a column which is 1.5 column wide? I recently came across a scenario where I wanted my columns wo be slightly wider that 1 column... say 1.25&lt;/p&gt;

&lt;p&gt;In order to achieve this, I created a &lt;a href="https://github.com/HoomanBahreini/bootstrap-half-and-quarter-grid/blob/main/README.md"&gt;custom bootstrap extension&lt;/a&gt;, which allows using fractions of columns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;In order to use &lt;strong&gt;bootstrap-half-and-quarter-grid&lt;/strong&gt; library, you just need to include the css file in your project. You can either copy/paste the file or use one of the following CDNs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn.jsdelivr.net/gh/hoomanbahreini/bootstrap-half-and-quarter-grid/fractional-grid.css"&gt;https://cdn.jsdelivr.net/gh/hoomanbahreini/bootstrap-half-and-quarter-grid/fractional-grid.css&lt;/a&gt; &lt;a href="https://cdn.jsdelivr.net/gh/hoomanbahreini/bootstrap-half-and-quarter-grid/fractional-grid.min.css"&gt;https://cdn.jsdelivr.net/gh/hoomanbahreini/bootstrap-half-and-quarter-grid/fractional-grid.min.css&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have included the library in your project then you can use its class... there are 3 main classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* 
 * replace star with a number between 0 and 11 
 */
col-*-1qtr 
col-*-half
col-*-3qtr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;
&lt;h2&gt;
  
  
  Example:
&lt;/h2&gt;

&lt;p&gt;`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// add a reference to: https://cdn.jsdelivr.net/gh/hoomanbahreini/bootstrap-half-and-quarter-grid/custom-bootstrap-grid.min.csc

&amp;lt;div class="container"&amp;gt;
  &amp;lt;h2&amp;gt;Using fraction of columns&amp;lt;/h2&amp;gt;
  &amp;lt;div class="row"&amp;gt;
    &amp;lt;div class="col-2-1qtr bg-success"&amp;gt;2 and quarter&amp;lt;/div&amp;gt;
    &amp;lt;div class="col-3-3qtr bg-warning"&amp;gt;3 and 3 quarter&amp;lt;/div&amp;gt;
    &amp;lt;div class="col-2-half bg-success"&amp;gt;2 and half&amp;lt;/div&amp;gt;
    &amp;lt;div class="col-3-half bg-warning"&amp;gt;3 and half&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me:
&lt;/h2&gt;

&lt;p&gt;I am software developer at &lt;a href="https://www.shopless.co.nz"&gt;Shopless online marketplace&lt;/a&gt; based in Wellington NZ.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Working With Files and Folders in S3, Using AWS SDK for .NET</title>
      <dc:creator>Hooman Bahraini</dc:creator>
      <pubDate>Sat, 07 Dec 2019 23:09:20 +0000</pubDate>
      <link>https://dev.to/hoomanbahreini/working-with-files-and-folders-in-s3-using-aws-sdk-for-net-205</link>
      <guid>https://dev.to/hoomanbahreini/working-with-files-and-folders-in-s3-using-aws-sdk-for-net-205</guid>
      <description>&lt;h2&gt;
  
  
  Introduction 
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I am going to show you how to use AWS SDK for .NET to do some basic file operations on an S3 bucket. AWS provides both &lt;em&gt;low-level&lt;/em&gt; and &lt;em&gt;high-level&lt;/em&gt; APIs. First, we are going to see how to use low-level APIs and then we will perform the same operations using high-level APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;In order to run the following code, you need to install the &lt;a href="https://www.nuget.org/packages/AWSSDK.S3/" rel="noopener noreferrer"&gt;AWSSDK.S3&lt;/a&gt; Nuget package.&lt;/p&gt;

&lt;p&gt;I have created an S3 bucket named: &lt;code&gt;my-bucket-name-123&lt;/code&gt; and I have created a folder named &lt;code&gt;my-folder&lt;/code&gt; inside the bucket:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bQ4nQTHG--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/1yz0xobui9py70gjl4m0.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--bQ4nQTHG--%2Fc_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000%2Fhttps%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1yz0xobui9py70gjl4m0.png" alt="enter image description here"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Low-Level APIs
&lt;/h2&gt;

&lt;p&gt;The low-level APIs are mapped closely to the underlying REST API, here we use a Request object to provide request information and AWS responds with the Response object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding S3 Path (when using low-level API)
&lt;/h2&gt;

&lt;p&gt;First, we need to understand that there is no concept of folder in S3, everything is an object. If I want to create a folder called &lt;code&gt;sub-folder&lt;/code&gt;, I need to append the folder name with a / to let AWS know that what I want is a folder, not a file... so I need to create:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;my-s3-bucket-name-123/my-folder/sub-folder/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If I don't include the trailing slash AWS will create an object called &lt;code&gt;sub-folder&lt;/code&gt; instead of a folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing &lt;code&gt;AmazonS3Client&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is how we initialize S3 clients, which we are going to use for the remaining examples:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string bucketName = "my-bucket-name-123";  
string awsAccessKey = "AKI............";  
string awsSecretKey = "+8Bo..................................";  

IAmazonS3 client = new AmazonS3Client(_awsAccessKey, _awsSecretKey, 
RegionEndpoint.APSoutheast2);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;AmazonS3Client&lt;/code&gt; is &lt;a href="https://stackoverflow.com/questions/7743488/is-the-amazon-net-aws-sdks-amazons3-thread-safe" rel="noopener noreferrer"&gt;thread safe&lt;/a&gt;, you can make it static or use a singletone instance.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a folder
&lt;/h2&gt;

&lt;p&gt;Here we are going to create a folder called &lt;code&gt;sub-folder&lt;/code&gt; inside &lt;code&gt;my-folder&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string folderPath = "my-folder/sub-folder/";

PutObjectRequest request = new PutObjectRequest()  
{  
    BucketName = _bucketName,  
    Key = folderPath // &amp;lt;-- in S3 key represents a path  
};  
PutObjectResponse response = client.PutObject(request);  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;If you forget the trailing slash in the path (i.e. &lt;code&gt;"my-folder/sub-folder"&lt;/code&gt;) it would create an object called &lt;code&gt;sub-folder&lt;/code&gt;... if you include a slash at the beginning of the path (i.e. &lt;code&gt;"/my-folder/sub-folder/"&lt;/code&gt;) it will create a folder with name as an empty string and put the remaining folders inside it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying a file into a folder
&lt;/h2&gt;

&lt;p&gt;The following code would copy test.txt inside sub-folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FileInfo file = new FileInfo(@"c:\test.txt");  
string path = "my-folder/sub-folder/test.txt";  

PutObjectRequest request = new PutObjectRequest()  
{  
    InputStream = file.OpenRead(),  
    BucketName = _bucketName,  
    Key = path // &amp;lt;-- in S3 key represents a path  
};  
PutObjectResponse response = client.PutObject(request);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Listing content of a folder
&lt;/h2&gt;

&lt;p&gt;The following code would list the contents of &lt;code&gt;sub-folder&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ListObjectsRequest request = new ListObjectsRequest  
{  
    BucketName = _bucketName,  
    Prefix = "my-folder/sub-folder/"  
};  

ListObjectsResponse response = client.ListObjects(request);    
foreach (S3Object obj in response.S3Objects)    
{    
    Console.WriteLine(obj.Key);    
}    

// result:  
// my-folder/sub-folder/  
// my-folder/sub-folder/test.txt  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Deleting file/folder
&lt;/h2&gt;

&lt;p&gt;In the following code first we delete &lt;code&gt;test.txt&lt;/code&gt; and then &lt;code&gt;sub-folder&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// delete test.txt file  
string filePath = "my-folder/sub-folder/test.txt";  

var deleteFileRequest = new DeleteObjectRequest  
{  
    BucketName = _bucketName,  
    Key = filePath  
};  
DeleteObjectResponse fileDeleteResponse = client.DeleteObject(deleteFileRequest);  

// delete sub-folder  
string folderPath = "my-folder/sub-folder/";  
var deleteFolderRequest = new DeleteObjectRequest  
{  
    BucketName = _bucketName,  
    Key = folderPath  
};  
DeleteObjectResponse folderDeleteResponse = client.DeleteObject(deleteFolderRequest);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  High-level APIs
&lt;/h2&gt;

&lt;p&gt;High-level APIs are designed to mimic the semantic of File I/O operations. They are very similar to working with &lt;code&gt;FileInfo&lt;/code&gt; and &lt;code&gt;Directory&lt;/code&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the S3 path (when using high-level APIs)
&lt;/h2&gt;

&lt;p&gt;When using high-level APIs, we need to use windows' styles paths, so use a backslash (NOT slash) in your path,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"my-folder\sub-folder\test.txt"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Also note that, similar to low-level APIs we need a trailing backslash to indicate a folder, for example, &lt;code&gt;"my-folder\sub-folder\"&lt;/code&gt; indicates that sub-folder is a folder whereas &lt;code&gt;"my-folder\sub-folder"&lt;/code&gt; indicates that sub-folder is an object inside my-folder. &lt;/p&gt;
&lt;h2&gt;
  
  
  Initializing &lt;code&gt;AmazonS3Client&lt;/code&gt; 
&lt;/h2&gt;

&lt;p&gt;Use the same code as low-level APIs (above) to initialize &lt;code&gt;AmazonS3Client&lt;/code&gt;.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a folder
&lt;/h2&gt;

&lt;p&gt;Here we are going to create a folder called &lt;code&gt;high-level-folder&lt;/code&gt; and create another folder called &lt;code&gt;my-folder&lt;/code&gt; inside it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string path = @"high-level-folder";

S3DirectoryInfo di = new S3DirectoryInfo(client, _bucketName, path);  
if (!di.Exists)  
{  
    di.Create();  
    di.CreateSubdirectory("sub-folder");  
}  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Copying file into folder
&lt;/h2&gt;

&lt;p&gt;The following code would copy &lt;code&gt;test.txt&lt;/code&gt; inside &lt;code&gt;sub-folder&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FileInfo localFile = new FileInfo(@"c:\test.txt");  
string path = @"high-level-folder\sub-folder\test.txt";  

S3FileInfo s3File = new S3FileInfo(client, _bucketName, path);  
if (!s3File.Exists)  
{  
    using (var s3Stream = s3File.Create()) // &amp;lt;-- create file in S3  
    {  
        localFile.OpenRead().CopyTo(s3Stream); // &amp;lt;-- copy the content to S3  
    }  
}  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Listing content of a folder
&lt;/h2&gt;

&lt;p&gt;The following code would list the content of a &lt;code&gt;sub-folder&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string path = @"high-level-folder\sub-folder\";  

S3DirectoryInfo di = new S3DirectoryInfo(client, _bucketName, path);  
IS3FileSystemInfo[] files = di.GetFileSystemInfos();  
foreach (S3FileInfo file in files)  
{  
    Console.WriteLine($"{file.Name}");  
}  

// result:  
// test.txt  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Unlike low-level API, here the folder name (sub-folder) is not listed.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting file/folder
&lt;/h2&gt;

&lt;p&gt;In the following code first we delete &lt;code&gt;test.txt&lt;/code&gt; and then &lt;code&gt;sub-folder&lt;/code&gt;:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;// delete test.txt file&lt;br&gt;
string filePath = @"high-level-folder\sub-folder\test.txt";&lt;/p&gt;

&lt;p&gt;S3FileInfo s3File = new S3FileInfo(client, _bucketName, filePath);&lt;br&gt;
if (s3File.Exists)&lt;br&gt;
{&lt;br&gt;
    s3File.Delete();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// delete sub-folder&lt;br&gt;
string folderPath = @"high-level-folder\sub-folder\";&lt;/p&gt;

&lt;p&gt;S3DirectoryInfo directory = new S3DirectoryInfo(client, _bucketName, folderPath);&lt;br&gt;
if (directory.Exists)&lt;br&gt;
{&lt;br&gt;
    directory.Delete();&lt;br&gt;
}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Wrapping up&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;When designing information systems, we follow a practice called &lt;a href="https://en.wikipedia.org/wiki/Single_source_of_truth" rel="noopener noreferrer"&gt;single source of truth&lt;/a&gt; (SSOT), it ensures that every data element is edited in only one place. SSOT simplifies the information system and makes working with it a lot easier. Personally I like to extend this practice to every aspect of design... when designing a UI, there is no point in giving users 2 different ways to buy a product, it complicates the UI and confuses the user... in my opinion, the same is true for designing a software library. I think by providing 2 different APIs (low-level and high-level), AWS has unnecessarily complicated the process of communicating with the S3 bucket, especially because these APIs use different paths systems... personally I spent hours on an error because I was using a high-level style path, with low-level APIs.&lt;/p&gt;

&lt;p&gt;Now, to make the matter worse, AWS is providing yet another way of interacting with S3 buckets, and that is using AWS TransferUtility which runs on top of low-level API and is the recommended way for reading/writing large objects (larger than a few GB). Have a look at &lt;a href="https://aws.amazon.com/blogs/developer/the-three-different-apis-for-amazon-s3/" rel="noopener noreferrer"&gt;this AWS documentation&lt;/a&gt; and see which one is the best option for your needs.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>dotnet</category>
      <category>files</category>
    </item>
  </channel>
</rss>
