CLOVER🍀

That was when it all began.

Ubuntu Linux 20.04 LTSに、.NET 5.0.NET Core 3.1をむンストヌルしおみる

これは、なにをしたくお曞いたもの

Ubuntu Linuxに、.NET Coreをむンストヌルしおみようかなず。

.NET Core

.NET自䜓の説明は、こちら。

.NET の概要 | Microsoft Docs

そしお、.NET Coreに぀いお。

2014 幎に、Microsoft によっお、.NET Framework のクロスプラットフォヌムのオヌプン゜ヌスの埌継版の䜜成が開始されたした。 この .NET の新しい実装には、バヌゞョン 3.1 に達するたで .NET Core ずいう名前が付けられおいたした。 .NET Core 3.1 以降の次のバヌゞョンは .NET 5.0 であり、珟圚プレビュヌ段階です。

.NET の抂芁 / .NET Core ず .NET 5

.NET Coreずいうのは、バヌゞョン3.1たでの名前ですが、クロスプラットフォヌムのオヌプン゜ヌス実装のこずを指すようです。
.NET 5以降は、"Core"ずは呌ばなくなるようですね。

で、.NET 5はプレビュヌ段階ず䞊蚘のペヌゞに曞いおいたすが、実際には2020幎11月にリリヌスされおいるようなので 。

Announcing .NET 5.0 | .NET Blog

[速報]マイクロソフト「.NET 5」正式リリース。1つのフレームワークでWindows/Mac/Linuxのデスクトップ、サーバアプリ、Webアプリなどが開発可能に - Publickey

Microsoftが .NET 5 をリリース

今回は、.NET 5.0ず.NET Core 3.1をむンストヌルしおみたいず思いたす。

環境

今回の環境は、こちらです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.2 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-70-generic #78-Ubuntu SMP Fri Mar 19 13:29:52 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Ubuntu Linux 20.04 LTSです。

Ubuntu Linuxに.NET 5.0をむンストヌルする

Linuxぞの.NETのむンストヌルに関するドキュメントは、こちら。

Linux ディストリビューションに .NET をインストールする | Microsoft Docs

Ubuntu Linuxず.NETのサポヌトマトリクス。

Linux に .NET をむンストヌルする / Ubuntu

むンストヌル方法はLinuxディストリビュヌションごずにドキュメントがあり、Ubuntu Linuxに぀いおはこちらです。

Ubuntu に .NET をインストールする - .NET | Microsoft Docs

サポヌトバヌゞョンに぀いおも再床蚘茉がありたす。

では、たずは.NET 5.0をむンストヌルしおみたす。

パッケヌゞ眲名キヌの远加。

$ curl -O -L -s https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb

リポゞトリ情報の曎新ず、必芁なパッケヌゞのむンストヌル。

$ sudo apt update && sudo apt install -y apt-transport-https

むンストヌル可胜なパッケヌゞに、.NETが含たれるようになっおいたす。こちらはSDKです。

$ apt search dotnet-sdk
゜ヌト䞭... 完了
党文怜玢... 完了  
dotnet-sdk-2.1/groovy 2.1.814-1 amd64
  Microsoft .NET Core SDK 2.1.814

dotnet-sdk-3.1/groovy 3.1.407-1 amd64
  Microsoft .NET Core SDK 3.1.407

dotnet-sdk-5.0/groovy 5.0.201-1 amd64
  Microsoft .NET SDK 5.0.201

ランタむムのみ。

$ apt search aspnetcore-runtime.*
゜ヌト䞭... 完了
党文怜玢... 完了  
aspnetcore-runtime-2.1/focal 2.1.26-1 amd64
  Microsoft ASP.NET Core 2.1.26 Shared Framework

aspnetcore-runtime-3.1/focal 3.1.13-1 amd64
  Shared Framework for hosting of Microsoft ASP.NET Core applications. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/aspnet/AspNetCore). We happily accept issues and PRs.

aspnetcore-runtime-5.0/focal 5.0.4-1 amd64
  Shared Framework for hosting of Microsoft ASP.NET Core applications. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/aspnetcore). We happily accept issues and PRs.

では、.NET 5.0のSDKをむンストヌル。
※ランタむムのみのむンストヌルも可胜なようです

$ sudo apt install dotnet-sdk-5.0

むンストヌルされたした。

$ dotnet --version
5.0.201

--list-sdksでむンストヌルされたSDKの䞀芧を、--list-runtimesでむンストヌルされたランタむムの䞀芧を芋るこずが
できたす。

$ dotnet --list-sdks
5.0.201 [/usr/share/dotnet/sdk]


$ dotnet --list-runtimes
Microsoft.AspNetCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

.NET Core 3.1をむンストヌルする

続いお、.NET Core 3.1をむンストヌルしおみたす。

$ sudo apt install dotnet-sdk-3.1

.NET 5.0を先にむンストヌルしおいるので、dotnetコマンドが瀺すバヌゞョンは5.0ずなっおいたす。

$ dotnet --version
5.0.201

ですが、SDKやランタむムずしおは認識されおいたす。

$ dotnet --list-sdks
3.1.407 [/usr/share/dotnet/sdk]
5.0.201 [/usr/share/dotnet/sdk]


$ dotnet --list-runtimes
Microsoft.AspNetCore.App 3.1.13 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.13 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

これで、.NET 5.0ず.NET Core 3.1の䞡方がむンストヌルされたこずになりたす。

そしお、この状態だずデフォルトは.NET 5.0が䜿われそうですね。

䜿っおみる

dotnetコマンドのヘルプを芋おみたしょう。

$ dotnet --help
.NET SDK (5.0.201)
䜿甚法: dotnet [runtime-options] [path-to-application] [arguments]

.NET アプリケヌションを実行したす。

runtime-options:
  --additionalprobingpath <path>   調査ポリシヌず調査察象アセンブリを含むパス。
  --additional-deps <path>         远加の deps.json ファむルぞのパス。
  --depsfile                       <application>.deps.json ファむルぞのパス。
  --fx-version <version>           アプリケヌションを実行するために䜿甚するむンストヌル枈み Shared Framework のバヌゞョン。
  --roll-forward <setting>         フレヌムワヌク バヌゞョン (LatestPatch、Minor、LatestMinor、Major、LatestMajor、Disable) にロヌルフォワヌドしたす。
  --runtimeconfig                  <application>.runtimeconfig.json ファむルぞのパス。

path-to-application:
  実行するアプリケヌション .dll ファむルぞのパス。

䜿甚法: dotnet [sdk-options] [command] [command-options] [arguments]

.NET SDK コマンドを実行したす。

sdk-options:
  -d|--diagnostics  蚺断出力を有効にしたす。
  -h|--help         コマンド ラむンのヘルプを衚瀺したす。
  --info            .NET 情報を衚瀺したす。
  --list-runtimes   むンストヌル枈みランタむムを衚瀺したす。
  --list-sdks       むンストヌル枈み SDK を衚瀺したす。
  --version         䜿甚䞭の .NET SDK バヌゞョンを衚瀺したす。

SDK コマンド:
  add               .NET プロゞェクトにパッケヌゞたたは参照を远加したす。
  build             .NET プロゞェクトをビルドしたす。
  build-server      ビルドによっお開始されたサヌバヌずやり取りしたす。
  clean             .NET プロゞェクトのビルド出力をクリヌンしたす。
  help              コマンド ラむンのヘルプを衚瀺したす。
  list              .NET プロゞェクトのプロゞェクト参照を䞀芧衚瀺したす。
  msbuild           Microsoft Build Engine (MSBuild) コマンドを実行したす。
  new               新しい .NET プロゞェクトたたはファむルを䜜成したす。
  nuget             远加の NuGet コマンドを提䟛したす。
  pack              NuGet パッケヌゞを䜜成したす。
  publish           .NET プロゞェクトを配眮のために公開したす。
  remove            .NET プロゞェクトからパッケヌゞたたは参照を削陀したす。
  restore           .NET プロゞェクトに指定されおいる䟝存関係を埩元したす。
  run               .NET プロゞェクトの出力をビルドしお実行したす。
  sln               Visual Studio ゜リュヌション ファむルを倉曎したす。
  store             指定されたアセンブリをランタむム パッケヌゞ ストアに栌玍したす。
  test              .NET プロゞェクトに指定されおいるテスト ランナヌを䜿甚しお、単䜓テストを実行したす。
  tool              .NET の゚クスペリ゚ンスを向䞊するツヌルをむンストヌルたたは管理したす。
  vstest            Microsoft Test Engine (VSTest) コマンドを実行したす。

バンドルされたツヌルからの远加コマンド:
  dev-certs         開発蚌明曞を䜜成し、管理したす。
  fsi               F# Interactive を開始するか、F# スクリプトを実行したす。
  sql-cache         SQL Server キャッシュ コマンドラむン ツヌル。
  user-secrets      開発ナヌザヌのシヌクレットを管理したす。
  watch             ファむルが倉曎されたずきにコマンドを実行するファむル りォッチャヌを起動したす。

コマンドに関する詳现情報に぀いおは、'dotnet [command] --help' を実行したす。

ビルドなどは、このコマンドでできそうですね。

では、こちらのドキュメントに沿っおコン゜ヌルアプリケヌション甚のプロゞェクトを䜜っおみたいず思いたす。

チュートリアル: .NET ツールを作成する - .NET CLI | Microsoft Docs

プロゞェクトを䜜成するdotnet newコマンドを実行しおみるず、䜜成できるプロゞェクトテンプレヌトが衚瀺されるようです。

$ dotnet new
Templates                                     Short Name           Language    Tags                  
--------------------------------------------  -------------------  ----------  ----------------------
Console Application                           console              [C#],F#,VB  Common/Console        
Class library                                 classlib             [C#],F#,VB  Common/Library        
WPF Application                               wpf                  [C#]        Common/WPF            
WPF Class library                             wpflib               [C#]        Common/WPF            
WPF Custom Control Library                    wpfcustomcontrollib  [C#]        Common/WPF            
WPF User Control Library                      wpfusercontrollib    [C#]        Common/WPF            
Windows Forms (WinForms) Application          winforms             [C#]        Common/WinForms       
Windows Forms (WinForms) Class library        winformslib          [C#]        Common/WinForms       
Worker Service                                worker               [C#],F#     Common/Worker/Web     
Unit Test Project                             mstest               [C#],F#,VB  Test/MSTest           
NUnit 3 Test Project                          nunit                [C#],F#,VB  Test/NUnit            
NUnit 3 Test Item                             nunit-test           [C#],F#,VB  Test/NUnit            
xUnit Test Project                            xunit                [C#],F#,VB  Test/xUnit            
Razor Component                               razorcomponent       [C#]        Web/ASP.NET           
Razor Page                                    page                 [C#]        Web/ASP.NET           
MVC ViewImports                               viewimports          [C#]        Web/ASP.NET           
MVC ViewStart                                 viewstart            [C#]        Web/ASP.NET           
Blazor Server App                             blazorserver         [C#]        Web/Blazor            
Blazor WebAssembly App                        blazorwasm           [C#]        Web/Blazor/WebAssembly
ASP.NET Core Empty                            web                  [C#],F#     Web/Empty             
ASP.NET Core Web App (Model-View-Controller)  mvc                  [C#],F#     Web/MVC               
ASP.NET Core Web App                          webapp               [C#]        Web/MVC/Razor Pages   
ASP.NET Core with Angular                     angular              [C#]        Web/MVC/SPA           
ASP.NET Core with React.js                    react                [C#]        Web/MVC/SPA           
ASP.NET Core with React.js and Redux          reactredux           [C#]        Web/MVC/SPA           
Razor Class Library                           razorclasslib        [C#]        Web/Razor/Library     
ASP.NET Core Web API                          webapi               [C#],F#     Web/WebAPI            
ASP.NET Core gRPC Service                     grpc                 [C#]        Web/gRPC              
dotnet gitignore file                         gitignore                        Config                
global.json file                              globaljson                       Config                
NuGet Config                                  nugetconfig                      Config                
Dotnet local tool manifest file               tool-manifest                    Config                
Web Config                                    webconfig                        Config                
Solution File                                 sln                              Solution              
Protocol Buffer File                          proto                            Web/gRPC              

Examples:
    dotnet new mvc --auth Individual
    dotnet new xunit 
    dotnet new --help
    dotnet new classlib --help

consoleがコン゜ヌルアプリケヌションのテンプレヌトです。

さらにヘルプを芋おみたす。

$ dotnet new console --help
䜿甚法: new [options]

オプション:
  -h, --help          Displays help for this command.
  -l, --list          Lists templates containing the specified template name. If no name is specified, lists all templates.
  -n, --name          The name for the output being created. If no name is specified, the name of the output directory is used.
  -o, --output        Location to place the generated output.
  -i, --install       Installs a source or a template pack.
  -u, --uninstall     Uninstalls a source or a template pack.
  --interactive       Allows the internal dotnet restore command to stop and wait for user input or action (for example to complete authentication).
  --nuget-source      Specifies a NuGet source to use during install.
  --type              Filters templates based on available types. Predefined values are "project" and "item".
  --dry-run           Displays a summary of what would happen if the given command line were run if it would result in a template creation.
  --force             Forces content to be generated even if it would change existing files.
  -lang, --language   Filters templates based on language and specifies the language of the template to create.
  --update-check      Check the currently installed template packs for updates.
  --update-apply      Check the currently installed template packs for update, and install the updates.


Console Application (C#)
Author: Microsoft
Description: A project for creating a command-line application that can run on .NET Core on Windows, Linux and macOS
Options:                                                                             
  -f|--framework  The target framework for the project.                              
                      net5.0           - Target net5.0                               
                      netcoreapp3.1    - Target netcoreapp3.1                        
                  Default: net5.0                                                    

  --langVersion   Sets the LangVersion property in the created project file          
                  text - Optional                                                    

  --no-restore    If specified, skips the automatic restore of the project on create.
                  bool - Optional                                                    
                  Default: false                                                     

オプションを芋おみるず、䜜成するプロゞェクトで䜿う.NETのバヌゞョンなども指定できそうですね。

では、プロゞェクトを䜜成したす。

$ dotnet new console -n my-console-project
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on my-console-project/my-console-project.csproj...
  埩元察象のプロゞェクトを決定しおいたす...
  path/to/my-console-project/my-console-project.csproj を埩元したした (71 ms)。
Restore succeeded.

--nameたたは-nオプションでプロゞェクト名を指定するず、指定された名前のディレクトリ内に必芁なファむルが
䜜成されたす。プロゞェクト名を指定しない堎合は、カレントディレクトリにファむルが生成されるようです。

プロゞェクト内に移動。

$ cd my-console-project

䜜成されたプロゞェクトファむル。

my-console-project.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <RootNamespace>my_console_project</RootNamespace>
  </PropertyGroup>

</Project>

生成されたプログラム。

Program.cs

using System;

namespace my_console_project
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

.NETのバヌゞョンを出力するように、内容を倉曎しおみたしょう。

Program.cs

using System;

namespace my_console_project
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello .NET Core {0}!", Environment.Version.ToString());
        }
    }
}

実行。

$ dotnet run
Hello .NET Core 5.0.4!

動䜜したした。

今床は、ビルドしお実行可胜ファむルを䜜成しおみたしょう。

$ dotnet build
.NET 向け Microsoft (R) Build Engine バヌゞョン 16.9.0+57a23d249
Copyright (C) Microsoft Corporation.All rights reserved.

  埩元察象のプロゞェクトを決定しおいたす...
  埩元察象のすべおのプロゞェクトは最新です。
  my-console-project -> /path/to/my-console-project/bin/Debug/net5.0/my-console-project.dll

ビルドに成功したした。
    0 個の譊告
    0 ゚ラヌ

経過時間 00:00:01.01

䜜成されたようなので、実行。

$ bin/Debug/net5.0/my-console-project
Hello .NET Core 5.0.4!

OKですね。

次は、䜿甚する.NETのバヌゞョンを倉曎しおみようかなず思いたす。

使用する .NET のバージョンを選択する - .NET | Microsoft Docs

こちらを芋るず、プロゞェクトファむルを倉曎しおもよさそうですね。しかも、耇数バヌゞョン指定できそうです。

チュヌトリアル: .NET CLI を䜿甚しお .NET ツヌルを䜜成する / プロゞェクトを䜜成する

今回は、2぀の.NETバヌゞョンを指定するようにしおみたしょう。倉曎前のプロゞェクトファむルはこちら。

my-console-project.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>
    <RootNamespace>my_console_project</RootNamespace>
  </PropertyGroup>

</Project>

TargetFrameworkの郚分を

    <TargetFramework>net5.0</TargetFramework>

TargetFrameworks耇数圢に倉曎しお、䜿甚する.NETのバヌゞョンを;区切りで远加したす。

    <TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>

ここで指定する名前は、こちらのペヌゞの「タヌゲット フレヌムワヌク モニカヌTFM」で衚珟される名前の
ようですね。

SDK スタイル プロジェクトでのターゲット フレームワーク - .NET | Microsoft Docs

ビルド。

$ dotnet build
.NET 向け Microsoft (R) Build Engine バヌゞョン 16.9.0+57a23d249
Copyright (C) Microsoft Corporation.All rights reserved.

  埩元察象のプロゞェクトを決定しおいたす...
  /path/to/my-console-project/my-console-project.csproj を埩元したした (83 ms)。
  my-console-project -> /path/to/my-console-project/bin/Debug/netcoreapp3.1/my-console-project.dll
  my-console-project -> /path/to/my-console-project/bin/Debug/net5.0/my-console-project.dll

ビルドに成功したした。
    0 個の譊告
    0 ゚ラヌ

経過時間 00:00:01.64

各.NETバヌゞョン向けの実行可胜ファむルが生成されたした。

確認。

$ bin/Debug/net5.0/my-console-project
Hello .NET Core 5.0.4!


$ bin/Debug/netcoreapp3.1/my-console-project
Hello .NET Core 3.1.13!

それぞれの.NETのバヌゞョンで、ビルドできたようです。

ちなみに、このようにTargetFrameworksで耇数の.NETのSDKを指定した状態だず、dotnet runだけでは実行できなくなりたす。

$ dotnet run
プロゞェクトを実行できたせん
プロゞェクトは耇数のフレヌムワヌクを察象ずしおいたす。'--framework' を䜿甚しお、実行するフレヌムワヌクを指定しおください。

䜿甚する.NETのバヌゞョンを指定する必芁があるようです。メッセヌゞにあるように--frameworkたたは-fオプションで
実行する.NETを指定したす。

$ dotnet run -f net5.0
Hello .NET Core 5.0.4!


$ dotnet run -f netcoreapp3.1
Hello .NET Core 3.1.13!

ずりあえずは、耇数バヌゞョンの.NETをUbuntu Linuxにむンストヌルしお、実行できたした、ず。

Azure Storage向けのSpring Boot Starterを詊しおみる

これは、なにをしたくお曞いたもの

ロヌカルで、Azure Storage゚ミュレヌタヌのAzuriteの䜿い方を調べたので、今床はJavaから䜿っおみたす。

Azure Storageエミュレーター、Azuriteを試す - CLOVER🍀

Azureで䜿えるSpringラむブラリがあるようなので、こちらを利甚しおみたしょう。

Azure 上の Spring 統合のドキュメント | Microsoft Docs

AzureでのSpring、Spring Boot

AzureでのSpringに関する情報は、こちらのドキュメントを参照するこずになりたす。

Azure 上の Spring 統合のドキュメント | Microsoft Docs

Spring Bootに぀いおは、こちら。

Azure 向けの Spring Boot Starter | Microsoft Docs

ただ、このペヌゞの情報は叀いようで、APIリファレンスのペヌゞを芋おいくのが珟時点では正解なようです。

https://docs.microsoft.com/en-us/java/api/overview/azure/spring-boot-readme?view=azure-java-stable

ガむド本䜓の方は、Spring Initializrを䜿っおAzureに関する䟝存関係を含めるこずになっおいたすが、Spring Boot 2.4以降では
Spring Initializrでは指定できず、pom.xmlなどに盎接䟝存関係を曞くこずになりたす。

Spring Cloud Azureのペヌゞを芋おもAzure偎のドキュメントに行くこずになっおいたすし、䞻䜓はAzure偎なんでしょうね。

Spring Cloud Azure

AzureでのSpringに関する゜ヌスコヌドは、Azure SDK for Javaのリポゞトリに含たれおいたす。

GitHub - Azure/azure-sdk-for-java: This repository is for active development of the Azure SDK for Java. For consumers of the SDK we recommend visiting our public developer docs at https://docs.microsoft.com/en-us/java/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-java.

珟時点でのAzureでのSpring Boot Starterのバヌゞョンが3.3.0なので、このバヌゞョンで参照するず 、こちらですね。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring

タグがバヌゞョンで现かくたくさんあるので、どれを芋るのが正解かむマむチわかっおいたせんが 。

ここを芋おいるず、実際に䜿うStarterのバヌゞョンごずにタグを指定するのが、厳密な気はしたすね。

For each package we release there will be a unique git tag created that contains the name and the version of the package to mark the commit of the code that produced the package. This tag will be used for servicing via hotfix branches as well as debugging the code for a particular preview or stable release version. Format of the release tags are <package-name>_<package-version>.

Azure SDK for Java / Release branches (Release tagging)

サンプルコヌドは、こちらにありたす。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot-samples

ですが、プロゞェクト単䜍のサンプルコヌドは存圚する堎合は以䞋のルヌルに沿ったディレクトリを芋るのが
正解のようです。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/samples

AzureでのSpring Bootに関する゜ヌスコヌドは、こちらを芋るずよいでしょう。
※Starterは、同じディレクトリ階局に䞊んでいたす

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot

AutoConfigureはこちら。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure

Spring Bootのサンプル。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot/src/samples

あず、AzureのJava SDK本䜓の方も芋るこずになるでしょう。

Azure SDK for Java を使用する作業の開始 | Microsoft Docs

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk

APIリファレンスは、こちら。

Reference | Microsoft Docs

APIリファレンスペヌゞは、英語版を芋た方が良さそうです。

ここたでが、AzureでのSpring Bootに関する情報でした。

Azure Storage向けのSpring Boot Starter

AzureでのSpring Bootでは、いく぀かのリ゜ヌスに察しおのStarterが提䟛されおいたす。

Azure Storage、Azure Key Vault、Azure Service Bus、Azure Cosmos DB、Azure Active Directory などなど。

今回はAzure Storageを䜿うSpring Boot Starterを芋おいきたす。

Azure Storage 用の Spring Boot Starter の使用方法 | Microsoft Docs

ただ、こちらも実際の蚭定方法はAPIリファレンス偎英語を芋た方が良さそうです。

https://docs.microsoft.com/en-us/java/api/overview/azure/spring-boot-starter-storage-readme?view=azure-java-stable

このペヌゞは、StarterのREADME.mdが元になっおいるようです。

https://github.com/Azure/azure-sdk-for-java/blob/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot-starter-storage/README.md

Azure Storageに関するAutoConfigureはこちら。

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/storage

あず、操䜜するのは結局Azure StorageのJava SDK APIなので、こちらも芋るこずになるでしょう。

Reference | Microsoft Docs

Azure Storage libraries for Java | Microsoft Docs

クイックスタート: Azure Blob Storage ライブラリ v12 - Java | Microsoft Docs

サンプルに぀いおは、Azure Storage向けのSpring Boot Starterに関するものはなさそうなので、Azure Java SDKで
Azure Storageを盎接扱うサンプルを芋るこずになるでしょう。

azure-sdk-for-java/sdk/storage/azure-storage-blob at azure-spring-boot_3.3.0 · Azure/azure-sdk-for-java · GitHub

https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.3.0/sdk/storage/azure-storage-blob/src/samples

では、情報をざっず眺めたずころで䜿っおいっおみたしょう。

環境

今回の環境は、こちらです。

$ java --version
openjdk 11.0.10 2021-01-19
OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.10, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-70-generic", arch: "amd64", family: "unix"

䜿甚するAzuriteのバヌゞョン。

$ npx azurite --version
3.11.0

プロゞェクトの䜜成

Spring Initializrで、Spring Bootプロゞェクトを䜜成したす。䟝存関係は、Spring WebFluxにしたした。
Azure Storageは、非同期のAPIを遞択するずReactorを䜿うこずになりたす。

$ curl -s https://start.spring.io/starter.tgz \
  -d dependencies=webflux,devtools \
  -d groupId=org.littlewings \
  -d artifactId=azure-spring-storege-example \
  -d packageName=org.littlewings.spring.azure \
  -d bootVersion=2.4.3 \
  -d javaVersion=11 \
  -d type=maven-project \
  -d baseDir=azure-spring-storege-example | tar zxvf -
$ cd azure-spring-storege-example

Azure向けのSpring Bootが䟝存しおいるSpring Bootのバヌゞョンは2.4.3ですが、倖郚から異なるバヌゞョンを指定されるこずも
想定しおいるようなので、珟時点でのSpring Bootの最新版である2.4.4にしおおきたした。

https://github.com/Azure/azure-sdk-for-java/blob/azure-spring-boot_3.3.0/sdk/spring/azure-spring-boot/pom.xml#L31

そしお、生成されたpom.xmlに以䞋の䟝存関係を远加したす。

     <dependency>
            <groupId>com.azure.spring</groupId>
            <artifactId>azure-spring-boot-starter-storage</artifactId>
            <version>3.3.0</version>
        </dependency>

これで、Azure Storage向けのSpring Boot Starterが䜿えるようになりたす。

自動生成された゜ヌスコヌドは、今回は䞍芁なので削陀しおおきしょう。

$ rm src/main/java/org/littlewings/spring/azure/DemoApplication.java src/test/java/org/littlewings/spring/azure/DemoApplicationTests.java

ずりあえず、mainクラスのみ再䜜成。

src/main/java/org/littlewings/spring/azure/App.java

package org.littlewings.spring.azure;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

Azure Storage向けのSpring Boot Starterの蚭定を行う

たずは、Azure Storage向けのSpring Boot Starterの蚭定を行いたす。

こちらを参考に。

Azure Spring Boot Starter Storage client library for Java / Auto-configuration for Azure Blob storage

今回はBlob Storageのみを䜿うこずにしたす。なので、Azuriteの情報を䜿っおこのように蚭定。

src/main/resources/application.properties

azure.storage.accountName=devstoreaccount1
azure.storage.accountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
azure.storage.blob-endpoint=http://127.0.0.1:10000/devstoreaccount1

これで、BlobServiceClientBuilderをDIできるようになりたす。

Azure Spring Boot Starter Storage client library for Java / Autowire the BlobServiceClientBuilder

存圚するリ゜ヌスであれば、Resouceず@ValueでDIできそうではありたすが。

Azure Spring Boot Starter Storage client library for Java / Autowire a resource

今回はBlobServiceClientBuilderを䜿いたす。

Azure StorageにアクセスするRestControllerを曞く

あずは、BlobServiceClientBuilderを䜿った゜ヌスコヌドを曞いおいくだけです。

RestControllerから、BlobServiceClientBuilderを䜿うようにしたしょう。たた、APIは非同期の方を䜿いたす。

こんな感じで雛圢を甚意。

src/main/java/org/littlewings/spring/azure/BlobController.java

package org.littlewings.spring.azure;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;

import com.azure.storage.blob.BlobContainerAsyncClient;
import com.azure.storage.blob.BlobServiceAsyncClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.models.ParallelTransferOptions;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class BlobController {
    BlobServiceClientBuilder blobServiceClientBuilder;

    BlobServiceAsyncClient serviceClient;

    public BlobController(BlobServiceClientBuilder blobServiceClientBuilder) {
        this.blobServiceClientBuilder = blobServiceClientBuilder;

        serviceClient = this.blobServiceClientBuilder.buildAsyncClient();
    }

    // ここに、メ゜ッドを曞く
}

BlobServiceClientBuilderをむンゞェクションしお、BlobServiceAsyncClientを䜜成したす。

続いお、Blobにアクセスするコヌド。URLは、{コンテナ名/Blob名`でアクセスするようにしおいたす。ずいっおも、コンテナの
階局的なパスを扱えうようにはしおいたせんけど。
それぞれ、Blobを取埗、アップロヌド、削陀するメ゜ッドです。

    // blobs
    @GetMapping("{containerName}/{blobName}")
    public Mono<String> getBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .getBlobAsyncClient(blobName)
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMapMany(blobClient -> blobClient
                        .download()
                        .map(bytes -> {
                            byte[] byteArray = new byte[bytes.remaining()];
                            bytes.get(byteArray);
                            return new String(byteArray, StandardCharsets.UTF_8);
                        })
                )
                .next();
    }

    @PostMapping("{containerName}/{blobName}")
    public Mono<String> uploadBlob(@PathVariable String containerName, @PathVariable String blobName, @RequestBody String body) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        ParallelTransferOptions options = new ParallelTransferOptions();

        return containerClient
                .exists()
                .flatMap(exists -> !exists ? serviceClient.createBlobContainer(containerName) : Mono.just(containerClient))
                .map(c -> c.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.upload(Flux.just(ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8))), options, true))
                .map(v -> String.format("blob uploaded, %s/%s", containerName, blobName));
    }

    @DeleteMapping("{containerName}/{blobName}")
    public Mono<String> deleteBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.delete())
                .map(v -> String.format("blob deleted, %s/%s", containerName, blobName));
    }

Azure Storageを䜿うサンプルコヌドは、同期のものばかりだったのず、Springで扱うものはなかったのでちょっず頑匵っお
曞いおみたした。

クイックスタート: Azure Blob Storage ライブラリ v12 - Java | Microsoft Docs

非同期の方を䜿うコヌドは、Javadoc甚のものがあるくらいでしょう。

https://github.com/Azure/azure-sdk-for-java/blob/azure-spring-boot_3.3.0/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobAsyncClientJavaDocCodeSnippets.java

わかりにくいですが、アップロヌドするコヌドのみ、コンテナが未存圚の堎合は䜜成するようにしおいたす。

    @PostMapping("{containerName}/{blobName}")
    public Mono<String> uploadBlob(@PathVariable String containerName, @PathVariable String blobName, @RequestBody String body) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        ParallelTransferOptions options = new ParallelTransferOptions();

        return containerClient
                .exists()
                .flatMap(exists -> !exists ? serviceClient.createBlobContainer(containerName) : Mono.just(containerClient))
                .map(c -> c.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.upload(Flux.just(ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8))), options, true))
                .map(v -> String.format("blob uploaded, %s/%s", containerName, blobName));
    }

この郚分ですね。

                .flatMap(exists -> !exists ? serviceClient.createBlobContainer(containerName) : Mono.just(containerClient))

BlobContainerAsyncClient#createずいうメ゜ッドもあるのですが、だいぶハマったのでこちらで 。

確認。

$ mvn spring-boot:run

アップロヌド。

$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/mycontainer/message.txt -d 'Hello World!!'
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 38

blob uploaded, mycontainer/message.txt


$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/mycontainer/message2.txt -d 'Hello Storage!!'
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 39

blob uploaded, mycontainer/message2.txt


$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/mycontainer2/foo.txt -d 'Foo'
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 35

blob uploaded, mycontainer2/foo.txt


$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/mycontainer2/hoge.txt -d 'Hoge'
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 36

blob uploaded, mycontainer2/hoge.txt


$ curl -i -XPOST -H 'Content-Type: text/plain' localhost:8080/mycontainer3/sample.txt -d 'Sample'
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 38

blob uploaded, mycontainer3/sample.txt

取埗。

    @GetMapping("{containerName}/{blobName}")
    public Mono<String> getBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .getBlobAsyncClient(blobName)
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMapMany(blobClient -> blobClient
                        .download()
                        .map(bytes -> {
                            byte[] byteArray = new byte[bytes.remaining()];
                            bytes.get(byteArray);
                            return new String(byteArray, StandardCharsets.UTF_8);
                        })
                )
                .next();
    }
$ curl -i localhost:8080/mycontainer/message.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 13

Hello World!!


$ curl -i localhost:8080/mycontainer/message2.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 15

Hello Storage!!


$ curl -i localhost:8080/mycontainer2/foo.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 3

Foo


$ curl -i localhost:8080/mycontainer2/hoge.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 4

Hoge


$ curl -i localhost:8080/mycontainer3/sample.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 6

Sample

削陀。

    @DeleteMapping("{containerName}/{blobName}")
    public Mono<String> deleteBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.delete())
                .map(v -> String.format("blob deleted, %s/%s", containerName, blobName));
    }
$ curl -i -XDELETE localhost:8080/mycontainer2/hoge.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 0

Blobがない堎合に、404にはしたせんでした 。

$ curl -i localhost:8080/mycontainer2/hoge.txt
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 0

続いお、コンテナを扱うコヌドも曞いおみたしょう。こちらも、特に階局的なパスは意識しおいたせんが。

    // containers
    @GetMapping
    public Mono<List<String>> containers() {
        return serviceClient
                .listBlobContainers()
                .map(blobContainerItem -> blobContainerItem.getName())
                .collectList();
    }

    @GetMapping("{containerName}")
    public Mono<List<String>> blobs(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .listBlobs()
                .map(blobItem -> blobItem.getName())
                .collectList();
    }

    @DeleteMapping("{containerName}")
    public Mono<String> deleteContainer(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .flatMap(v -> containerClient.delete())
                .map(v -> String.format("container deleted, %s", containerName));
    }

珟圚のコンテナの䞀芧を取埗。

    @GetMapping
    public Mono<List<String>> containers() {
        return serviceClient
                .listBlobContainers()
                .map(blobContainerItem -> blobContainerItem.getName())
                .collectList();
    }
$ curl -i localhost:8080
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45

["mycontainer","mycontainer2","mycontainer3"]

コンテナ内に保持しおいるBlobの䞀芧を取埗。

    @GetMapping("{containerName}")
    public Mono<List<String>> blobs(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .listBlobs()
                .map(blobItem -> blobItem.getName())
                .collectList();
    }
$ curl -i localhost:8080/mycontainer
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 30

["message.txt","message2.txt"]

コンテナを削陀。

    @DeleteMapping("{containerName}")
    public Mono<String> deleteContainer(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .flatMap(v -> containerClient.delete())
                .map(v -> String.format("container deleted, %s", containerName));
    }
$ curl -i -XDELETE localhost:8080/mycontainer3
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 0

指定したコンテナが削陀されたした、ず。

$ curl -i localhost:8080
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 30

["mycontainer","mycontainer2"]

たずめ

Azure向けのSpring Boot Starterを䜿っお、Azure Storageにアクセスしおみたした。

けっこうおこずったのですが、AzureのSpringに関する情報はどこを芋たらいいのかずいうずころず、Reactorに䞍慣れな
ずころで苊劎した感じですね 。

やりたいずころたではできたので、良しずしたしょう。

最埌に、今回䜜成したAzure StorageにアクセスするRestControllerの゜ヌスコヌド党䜓を茉せおおきたす。

src/main/java/org/littlewings/spring/azure/BlobController.java

package org.littlewings.spring.azure;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;

import com.azure.storage.blob.BlobContainerAsyncClient;
import com.azure.storage.blob.BlobServiceAsyncClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.models.ParallelTransferOptions;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class BlobController {
    BlobServiceClientBuilder blobServiceClientBuilder;

    BlobServiceAsyncClient serviceClient;

    public BlobController(BlobServiceClientBuilder blobServiceClientBuilder) {
        this.blobServiceClientBuilder = blobServiceClientBuilder;

        serviceClient = this.blobServiceClientBuilder.buildAsyncClient();
    }

    // containers
    @GetMapping
    public Mono<List<String>> containers() {
        return serviceClient
                .listBlobContainers()
                .map(blobContainerItem -> blobContainerItem.getName())
                .collectList();
    }

    @GetMapping("{containerName}")
    public Mono<List<String>> blobs(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .listBlobs()
                .map(blobItem -> blobItem.getName())
                .collectList();
    }

    @DeleteMapping("{containerName}")
    public Mono<String> deleteContainer(@PathVariable String containerName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .flatMap(v -> containerClient.delete())
                .map(v -> String.format("container deleted, %s", containerName));
    }

    // blobs
    @GetMapping("{containerName}/{blobName}")
    public Mono<String> getBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .getBlobAsyncClient(blobName)
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMapMany(blobClient -> blobClient
                        .download()
                        .map(bytes -> {
                            byte[] byteArray = new byte[bytes.remaining()];
                            bytes.get(byteArray);
                            return new String(byteArray, StandardCharsets.UTF_8);
                        })
                )
                .next();
    }

    @PostMapping("{containerName}/{blobName}")
    public Mono<String> uploadBlob(@PathVariable String containerName, @PathVariable String blobName, @RequestBody String body) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        ParallelTransferOptions options = new ParallelTransferOptions();

        return containerClient
                .exists()
                .flatMap(exists -> !exists ? serviceClient.createBlobContainer(containerName) : Mono.just(containerClient))
                .map(c -> c.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.upload(Flux.just(ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8))), options, true))
                .map(v -> String.format("blob uploaded, %s/%s", containerName, blobName));
    }

    @DeleteMapping("{containerName}/{blobName}")
    public Mono<String> deleteBlob(@PathVariable String containerName, @PathVariable String blobName) {
        BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(containerName);

        return containerClient
                .exists()
                .filter(Boolean::booleanValue)
                .map(v -> containerClient.getBlobAsyncClient(blobName))
                .flatMap(blobClient -> blobClient.delete())
                .map(v -> String.format("blob deleted, %s/%s", containerName, blobName));
    }
}