はじめに
設置する機器が複数ある場合など、システムがある程度の規模になった場合、複数のアプリケーションを連携させたくなることがあります。
<例>
・画像をスキャンするアプリケーション
・送信された画像を演出に用いるアプリケーション
同じPCだけでなく、違うPCで連携させたいことがあります。
<例>
・画像撮影用PC
・演出用PC
シンプルなやり方をとりたいので、
.Net Coreでサーバーアプリケーションを作り、通信はhttp(あるいはhttps)を使い、アプリケーション間で連携することにします。
必要なもの
開発環境・実行環境用 共にWindows PCを使用し、
サーバー側PCの実行環境にIIS(Internet Information Services)を使用します。
クライアント機器は、http通信が出来るならばなんでも利用出来そうです。
※多数(具体的には20台より多く)の機器から接続する場合、
サーバーにデスクトップ用のWindowsを使用するとライセンス違反となる場合があるので、その場合はLinux等のOS上に実行環境を構築します。
サーバー側PC
サーバーアプリケーションを配置します。
.Net Coreのランタイム等が必要になります。
https://dotnet.microsoft.com/download/dotnet-core
・開発用に .NET Core SDK
・実行環境用にASP.NET Core Hosting Bundle
をダウンロードし、実行用にIISをインストールします。
クライアント側PC
クライアント側のアプリケーションに、サーバーアプリケーションと通信する機能を実装します。
サーバーアプリを配置しているPCと同じPCでも構いません。
サーバーアプリの作成
Visual Studio上で新しいプロジェクトを作成し、
プロジェクトテンプレートに「ASP .Net Core アプリケーション(C#)」を、
追加のプロジェクトテンプレートに「API」を選択してみます。
以下のクラスを追加します。
SampleItem.cs
public class SampleItem
{
/// <summary>
/// ID(連番)
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public uint ID { get; set; }
/// <summary>
/// 名前
/// </summary>
[MaxLength(128)]
public string Name { get; set; }
(略)
}
SampleDbContext.cs
public class SampleDbContext: DbContext
{
public DbSet<SampleItem> Samples { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=ImageDatabase.sqlite");
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SampleItem>().ToTable("SampleItem", "test");
base.OnModelCreating(modelBuilder);
}
また、Startupクラスを以下のように編集します。
Startup.cs
public class Startup
{
/// <summary>
/// appsettings.jsonなどから読み込んだこのアプリの設定情報
/// </summary>
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
// appsettings.jsonなどから読み込んだ設定
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSetting>(Configuration);
// DBコンテキストの作成
services.AddEntityFrameworkSqlite().AddDbContext<SampleDbContext>();
// DBのテーブルが無ければ作る。既にある場合はそのまま
using (var client = new SampleDbContext())
{
client.Database.EnsureCreated();
}
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
(※省略)
}
}
機能の実装
投稿した画像を保存する機能、取得する機能を実装します。
APIController.cs
[Serializable]
[DataContract]
public class ScanImageGetInfo
{
[DataMember(Name = "id")]
public uint ID { get; set; }
(略)
}
[HttpPost]
public IActionResult PostImage(IFormFile Targetfile, int MachineID, int ImageCodeID)
{
if (Targetfile == null)
{
return new BadRequestResult(); // HTTPステータスコード 400
}
string savePath = SaveFileToWorkFolder(Targetfile, MachineID, ImageCodeID);
//ファイルをダウンロード
return new EmptyResult(); // HTTPステータスコード 200
}
/// <summary>
/// 指定したIDの画像データを取得
/// </summary>
/// <param name="Id">PopImageで取得したレコードのID</param>
/// <returns></returns>
public IActionResult GetImage(int ID)
{
using (var context = new SampleDbContext())
{
var item = context.ScanImages.FirstOrDefault(x => x.ID == ID);
if (item == null) return new BadRequestResult();
filePath = folder + "\\" + item.FilePath;
fileName = item.Name;
}
return File(GetStream(filePath), "image/png", Path.GetFileName(fileName));
}
// ファイルをこのPC上に保存する関数
string SaveFileToWorkFolder(){
(略)
}
// FileStreamを全て読み込み、MemoryStreamとして返す
private Stream GetStream(String filepath)
{
var memory = new MemoryStream();
using (var stream = new FileStream(filepath, FileMode.Open))
{
stream.CopyTo(memory);
}
memory.Position = 0;
return memory;
}
これで/api/postimageにアクセスするとファイルの保存、/api/getimageにアクセスするとファイルの取得が出来るようになりました。
ファイルの投稿(クライアントスキャナアプリ側)
.Net Frameworkで作成したアプリケーションですが、
以下のように引数を与えてHttpのPostを送信しています。
画像の投稿を行うHttpPostImage()関数
async public Task<string> HttpPostImage(string imageFilePath, int imageCodeID)
{
//File.WriteAllText(System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\log2.txt",
// "postimage" + "\n");
// 送信を行います。
// POSTする情報
//WWWForm form = new WWWForm();
//form.AddField("userId", userId);
string APIURL = "http://"
+ Properties.Settings.Default.ServerURL
+ ":"
+ Properties.Settings.Default.ServerPort
+ "/API/PostImage";
using (var stream = File.OpenRead(imageFilePath))
using (var fileContent = new StreamContent(stream))
using (var content = new MultipartFormDataContent())
using (var client = new HttpClient())
{
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")
{
Name = "Targetfile",
FileName = Path.GetFileName(imageFilePath),
};
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
content.Add(fileContent);
content.Add(new StringContent(Properties.Settings.Default.MachineID.ToString()), "MachineID");
content.Add(new StringContent(imageCodeID.ToString()), "ImageCodeID");
using (var response = await client.PostAsync(APIURL, content))
{
return await response.Content.ReadAsStringAsync();
}
}
}
ファイルの取得(クライアント演出アプリ側)
Unityのアプリケーションですが、以下のようにしてGetを送信し、テクスチャ画像を取得することが出来ます。
画像の取得を行うコルーチン関数
IEnumerator AccessCoroutine()
{
// Request
string getImageUri = "http://" + _serverURL + ":" + _serverPort + ServerGetImageAPI;
// 処理終わるまでループ
while (isRunning)
{
(略 idに取得したい画像のidを入れる)
string queryString = "?id=" + id;
// Texture2Dを作成
using (UnityWebRequest webGetImageRequest = UnityWebRequestTexture.GetTexture(getImageUri + queryString, true))
{
yield return webGetImageRequest.SendWebRequest();
if (webGetImageRequest.isNetworkError || webGetImageRequest.isHttpError)
{
Debug.Log(webGetImageRequest.error);
}
else
{
// Get downloaded asset bundle
Texture2D texture = DownloadHandlerTexture.GetContent(webGetImageRequest);
(略 テクスチャー画像の処理)
}
}
}
}
yield return new WaitForSeconds(1.0f);
}
}
これらのプログラムを組み合わせて、PC間で連携したひとつのシステムを作ることが出来ます。