{"id":2764,"date":"2023-07-25T23:29:56","date_gmt":"2023-07-26T06:29:56","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=2764"},"modified":"2023-07-25T23:29:56","modified_gmt":"2023-07-26T06:29:56","slug":"announcing-the-stable-release-of-the-azure-files-client-library-for-go","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/announcing-the-stable-release-of-the-azure-files-client-library-for-go\/","title":{"rendered":"Announcing the stable release of the Azure Files client library for Go"},"content":{"rendered":"<p>The Azure SDK for Go team at Microsoft is excited to announce the stable release of the <a href=\"https:\/\/pkg.go.dev\/github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\">Azure Files client library for Go<\/a>. <a href=\"https:\/\/azure.microsoft.com\/products\/storage\/files\/\">Azure Files<\/a> offers fully managed file shares in the cloud that are accessible via the industry standard <a href=\"https:\/\/learn.microsoft.com\/windows\/desktop\/FileIO\/microsoft-smb-protocol-and-cifs-protocol-overview\">Server Message Block (SMB)<\/a> protocol. Azure Files shares can be mounted concurrently by cloud or on-premises deployments of Windows, Linux, and macOS. Azure Files shares can also be cached on Windows Servers with Azure File Sync for fast access near where the data is being used.<\/p>\n<blockquote><p>To upgrade from the legacy <a href=\"https:\/\/github.com\/Azure\/azure-storage-file-go\">Azure Files library for Go<\/a>, see the <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-go\/blob\/main\/sdk\/storage\/azfile\/migrationguide.md\">migration guide<\/a>.<\/p><\/blockquote>\n<h2>Install the package<\/h2>\n<p>The Azure Files client library is named <code>azfile<\/code>. To install the latest version of <code>azfile<\/code>, use the following <code>go get<\/code> command:<\/p>\n<pre><code class=\"language-bash\">go get github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile<\/code><\/pre>\n<p>We assume that you have:<\/p>\n<ul>\n<li>An Azure subscription.<\/li>\n<li>A working development environment for Go version 1.18 or above.<\/li>\n<\/ul>\n<h2>Create a client<\/h2>\n<p><code>Client<\/code> is a type that exposes methods that invoke service operations. A <code>Client<\/code> type is shareable between multiple goroutines simultaneously and safely.<\/p>\n<p>Azure Files offers four types of resources: the storage account, the shares within the account, and the directories and files within a share. Instances of the <code>service.Client<\/code>, <code>share.Client<\/code>, <code>directory.Client<\/code>, and <code>file.Client<\/code> types provide methods to manipulate shares, directories, and files within a storage account. You must specify the storage account when you create a client.<\/p>\n<p>You can create a client using an Azure Files connection string or access key or with a Shared Access Signature(SAS) (obtained via the Azure portal).<\/p>\n<h3>Use Azure Files connection string<\/h3>\n<p>Azure Files supports authentication using a connection string, which you can get from the Azure portal.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/service\"\r\n\r\nfunc main() {\r\n    serviceClient, err := service.NewClientFromConnectionString(\"&lt;Azure Storage Connection String&gt;\", nil)\r\n    if err != nil {\r\n        \/\/TODO: handle error\r\n        panic(err)\r\n    }\r\n}<\/code><\/pre>\n<h3>Use the <code>SharedKeyCredential<\/code><\/h3>\n<p>Azure Files supports authentication using access key, which you can get from the Azure portal.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/service\"\r\n\r\nfunc main() {\r\n    \/\/ create a credential for authenticating using shared key\r\n    cred, err := service.NewSharedKeyCredential(\"&lt;my-storage-account-name&gt;\", \"&lt;my-storage-account-key&gt;\")\r\n    if err != nil {\r\n        \/\/TODO: handle error\r\n        panic(err)\r\n    }\r\n\r\n    \/\/ create service.Client for the specified storage account that uses the above credential\r\n    serviceClient, err := service.NewClientWithSharedKeyCredential(\"https:\/\/&lt;my-storage-account-name&gt;.file.core.windows.net\/\", cred, nil)\r\n    if err != nil {\r\n        \/\/TODO: handle error\r\n        panic(err)\r\n    }\r\n}<\/code><\/pre>\n<h3>Use the Shared Access Signature (SAS) token<\/h3>\n<p>Azure Files supports authentication using SAS token, which you can get from the Azure portal.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/service\"\r\n\r\nfunc main() {\r\n    serviceClient, err := service.NewClientWithNoCredential(\"https:\/\/&lt;my-storage-account-name&gt;.file.core.windows.net\/?&lt;sas token&gt;\", nil)\r\n    if err != nil {\r\n        \/\/TODO: handle error\r\n        panic(err)\r\n    }\r\n}<\/code><\/pre>\n<h2>Share operations<\/h2>\n<p>The <code>share.Client<\/code> type exposes share operations that manipulate the lifecycle of the Azure Files share.<\/p>\n<h3>Create a share<\/h3>\n<p>The <code>share<\/code> package&#8217;s <code>Create<\/code> method creates a new share under the specified account. If a share with the same name already exists, the method call raises a <code>ResourceAlreadyExists<\/code> error.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"fmt\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/share\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    accountName, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_NAME\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_NAME could not be found\")\r\n    }\r\n    accountKey, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_KEY\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_KEY could not be found\")\r\n    }\r\n\r\n    shareName := \"testshare\"\r\n    shareURL := fmt.Sprintf(\"https:\/\/%s.file.core.windows.net\/%s\", accountName, shareName)\r\n\r\n    cred, err := share.NewSharedKeyCredential(accountName, accountKey)\r\n    handleError(err)\r\n\r\n    shareClient, err := share.NewClientWithSharedKeyCredential(shareURL, cred, nil)\r\n    handleError(err)\r\n\r\n    _, err = shareClient.Create(context.TODO(), nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h3>Delete a share<\/h3>\n<p>The <code>share<\/code> package&#8217;s <code>Delete<\/code> method is a share lifecycle method that marks the specified share for deletion. During garbage collection, Azure deletes the share and any directories\/files within it. If the share is missing, the method call raises a <code>ResourceNotFound<\/code> error.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"fmt\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/share\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    accountName, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_NAME\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_NAME could not be found\")\r\n    }\r\n    accountKey, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_KEY\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_KEY could not be found\")\r\n    }\r\n\r\n    shareName := \"testshare\"\r\n    shareURL := fmt.Sprintf(\"https:\/\/%s.file.core.windows.net\/%s\", accountName, shareName)\r\n\r\n    cred, err := share.NewSharedKeyCredential(accountName, accountKey)\r\n    handleError(err)\r\n\r\n    shareClient, err := share.NewClientWithSharedKeyCredential(shareURL, cred, nil)\r\n    handleError(err)\r\n\r\n    _, err = shareClient.Delete(context.TODO(), nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h2>Directory operations<\/h2>\n<p>The <code>directory.Client<\/code> type exposes operations that can be performed on directories in a file share.<\/p>\n<h3>Create a directory<\/h3>\n<p>The <code>directory<\/code> package&#8217;s <code>Create<\/code> method creates a new directory under the specified share. If a directory with the same name already exists, the method call raises a <code>ResourceAlreadyExists<\/code> error.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/directory\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    connectionString, ok := os.LookupEnv(\"AZURE_STORAGE_CONNECTION_STRING\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_CONNECTION_STRING could not be found\")\r\n    }\r\n    shareName := \"testShare\"\r\n    dirName := \"testDirectory\"\r\n    dirClient, err := directory.NewClientFromConnectionString(connectionString, shareName, dirName, nil)\r\n    handleError(err)\r\n\r\n    _, err = dirClient.Create(context.Background(), nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h3>Delete a directory<\/h3>\n<p>The <code>directory<\/code> package&#8217;s <code>Delete<\/code> method deletes the specified empty directory. If you attempt to delete directories that aren&#8217;t empty, the method call raises a <code>DirectoryNotEmpty<\/code> error. If the directory is missing, the method call raises a <code>ResourceNotFound<\/code> error.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/directory\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    connectionString, ok := os.LookupEnv(\"AZURE_STORAGE_CONNECTION_STRING\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_CONNECTION_STRING could not be found\")\r\n    }\r\n    shareName := \"testShare\"\r\n    dirName := \"testDirectory\"\r\n    dirClient, err := directory.NewClientFromConnectionString(connectionString, shareName, dirName, nil)\r\n    handleError(err)\r\n\r\n    _, err = dirClient.Delete(context.Background(), nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h2>File operations<\/h2>\n<p>The <code>file.Client<\/code> type exposes the operations that interact with the files that reside inside the Azure Files share or directory.<\/p>\n<h3>Upload file to a directory<\/h3>\n<p>The <code>UploadFile<\/code> method uploads a file in chunks to a file inside the Azure Files share or directory. This operation can be performed on an existing file only, otherwise it raises a <code>ResourceNotFound<\/code> error.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"fmt\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/file\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    \/\/ Set up file to upload\r\n    fileSize := 8 * 1024 * 1024\r\n    fileName := \"test_upload_file.txt\"\r\n    fileData := make([]byte, fileSize)\r\n    err := os.WriteFile(fileName, fileData, 0666)\r\n    handleError(err)\r\n\r\n    \/\/ Open the file to upload\r\n    fileHandler, err := os.Open(fileName)\r\n    handleError(err)\r\n\r\n    \/\/ delete the local file if required.\r\n    defer func(name string) {\r\n        err = os.Remove(name)\r\n        handleError(err)\r\n    }(fileName)\r\n\r\n    \/\/ close the file after it is no longer required.\r\n    defer func(fh *os.File) {\r\n        err = fh.Close()\r\n        handleError(err)\r\n    }(fileHandler)\r\n\r\n    accountName, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_NAME\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_NAME could not be found\")\r\n    }\r\n    accountKey, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_KEY\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_KEY could not be found\")\r\n    }\r\n\r\n    cred, err := file.NewSharedKeyCredential(accountName, accountKey)\r\n    handleError(err)\r\n\r\n    shareName := \"testShare\"\r\n    dirName := \"testDirectory\"\r\n\r\n    fileURL := fmt.Sprintf(\"https:\/\/%s.file.core.windows.net\/%s\/%s\/%s\", accountName, shareName, dirName, fileName)\r\n    fileClient, err := file.NewClientWithSharedKeyCredential(fileURL, cred, nil)\r\n    handleError(err)\r\n\r\n    _, err = fileClient.Create(context.Background(), int64(fileSize), nil)\r\n    handleError(err)\r\n\r\n    err = fileClient.UploadFile(context.Background(), fileHandler, nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h3>Download file from a directory<\/h3>\n<p><code>DownloadFile<\/code> downloads a file from the Azure Files share or directory to a local file that is passed as a parameter. If the size doesn&#8217;t match, the method truncates the destination file.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"fmt\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/file\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    \/\/ Set up file to download to\r\n    destFileName := \"test_download_file.txt\"\r\n    destFile, err := os.Create(destFileName)\r\n    handleError(err)\r\n    defer func(destFile *os.File) {\r\n        err = destFile.Close()\r\n        handleError(err)\r\n    }(destFile)\r\n\r\n    accountName, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_NAME\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_NAME could not be found\")\r\n    }\r\n    accountKey, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_KEY\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_KEY could not be found\")\r\n    }\r\n\r\n    cred, err := file.NewSharedKeyCredential(accountName, accountKey)\r\n    handleError(err)\r\n\r\n    shareName := \"testShare\"\r\n    dirName := \"testDirectory\"\r\n\r\n    fileURL := fmt.Sprintf(\"https:\/\/%s.file.core.windows.net\/%s\/%s\/%s\", accountName, shareName, dirName, destFileName)\r\n    fileClient, err := file.NewClientWithSharedKeyCredential(fileURL, cred, nil)\r\n    handleError(err)\r\n\r\n    _, err = fileClient.DownloadFile(context.Background(), destFile, nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h3>Delete file in a directory<\/h3>\n<p>The <code>file<\/code> package&#8217;s <code>Delete<\/code> method immediately removes the file from the storage account. For more information, see the <a href=\"https:\/\/learn.microsoft.com\/rest\/api\/storageservices\/delete-file2\">delete file documentation<\/a>.<\/p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"context\"\r\n    \"fmt\"\r\n    \"log\"\r\n    \"os\"\r\n\r\n    \"github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile\/file\"\r\n)\r\n\r\nfunc handleError(err error) {\r\n    if err != nil {\r\n        log.Fatal(err.Error())\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    accountName, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_NAME\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_NAME could not be found\")\r\n    }\r\n    accountKey, ok := os.LookupEnv(\"AZURE_STORAGE_ACCOUNT_KEY\")\r\n    if !ok {\r\n        panic(\"AZURE_STORAGE_ACCOUNT_KEY could not be found\")\r\n    }\r\n\r\n    cred, err := file.NewSharedKeyCredential(accountName, accountKey)\r\n    handleError(err)\r\n\r\n    shareName := \"testShare\"\r\n    dirName := \"testDirectory\"\r\n    fileName := \"testFile\"\r\n\r\n    fileURL := fmt.Sprintf(\"https:\/\/%s.file.core.windows.net\/%s\/%s\/%s\", accountName, shareName, dirName, fileName)\r\n    fileClient, err := file.NewClientWithSharedKeyCredential(fileURL, cred, nil)\r\n    handleError(err)\r\n\r\n    _, err = fileClient.Delete(context.Background(), nil)\r\n    handleError(err)\r\n}<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>The Azure Files library for Go allows users to manipulate files, directories and shares in Azure Storage. To learn more, see our <a href=\"https:\/\/pkg.go.dev\/github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile#section-readme\">documentation<\/a>. You can also find more examples either on <a href=\"https:\/\/pkg.go.dev\/github.com\/Azure\/azure-sdk-for-go\/sdk\/storage\/azfile#readme-examples\">pkg.go.dev<\/a> or in our <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-go\/tree\/main\/sdk\/storage\/azfile\">GitHub repository<\/a>.<\/p>\n<h2>Feedback<\/h2>\n<p>We&#8217;d love to hear about your experiences using the Azure SDK for Go. Send us your feedback on our <a href=\"https:\/\/gophers.slack.com\/archives\/CA7HK8EEP\">Slack Channel<\/a> or at the <a href=\"https:\/\/discordapp.com\/channels\/723347736853741589\/933781546815606885\">#golang-friends<\/a> channel on the Microsoft Open Source Discord Server.<\/p>\n<p>For feature requests, bug reports, or general support, <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-go\/issues\/new\/choose\">open an issue<\/a> in the Azure SDK for Go repository on GitHub.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Announcing the stable release of the Azure Files client library for Go, which allows users to manipulate files, directories, and shares in Azure Storage.<\/p>\n","protected":false},"author":121233,"featured_media":2769,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[912,913,810,811,854],"class_list":["post-2764","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-azure-storage-file","tag-files","tag-go","tag-golang","tag-stable-releases"],"acf":[],"blog_post_summary":"<p>Announcing the stable release of the Azure Files client library for Go, which allows users to manipulate files, directories, and shares in Azure Storage.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/121233"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=2764"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2764\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/2769"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=2764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=2764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=2764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}