diff --git a/ImageApi.Core/ImageParameter.cs b/ImageApi.Core/ImageParameter.cs index 8568514..a6c5cbb 100644 --- a/ImageApi.Core/ImageParameter.cs +++ b/ImageApi.Core/ImageParameter.cs @@ -30,7 +30,7 @@ public ImageParameter GetFixed(IImageParameterFixer fixer) public string GetRelativePath() { - var folder = $"App_Data\\storage\\images\\{this.Year}\\{this.Month}\\{this.Id}\\"; + var folder = $"App_Data/storage/images/{this.Year}/{this.Month}/{this.Id}/"; if (this.ImageFormat == ImageFormat.Gif) { return folder + "Full.gif"; @@ -40,7 +40,7 @@ public string GetRelativePath() public string GetVirtualPath() { - return "~\\"+GetRelativePath(); + return "~/"+GetRelativePath(); } } } diff --git a/ImageApi/Controllers/ImageController.cs b/ImageApi/Controllers/ImageController.cs index 3e89194..72a42f7 100644 --- a/ImageApi/Controllers/ImageController.cs +++ b/ImageApi/Controllers/ImageController.cs @@ -7,6 +7,7 @@ using ImageCore; using ImageCore.Enums; using ImageCore.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -51,7 +52,7 @@ public ImageController(IFileStore fileStore, IImageService imageService, ImageOp /// [Route("/img/{size}/t{imageType}t{yearMonth}-{id}.{format}")] [HttpGet] - [ResponseCache(Duration = 600, Location = ResponseCacheLocation.Any)] + [ResponseCache(Duration = 600, Location = ResponseCacheLocation.Client)] public async Task Index([FromServices] IImageParameterFixer parameterFixer, [FromRoute] ImageParameter parameter) { @@ -93,6 +94,8 @@ public async Task Index([FromServices] IImageParameterFixer param /// 例如 /img/s80x80/t20t201902-94E0437664E3FA99C094E0437664E3FA99C0.png /// [HttpPost] + [Authorize] + [DisableRequestSizeLimit] public async Task Upload([FromFile] ImageFileInfo file, bool isTemp = false, BusinessType businessType = BusinessType.Default) { diff --git a/ImageApi/Dockerfile b/ImageApi/Dockerfile index c52689d..86e47b7 100644 --- a/ImageApi/Dockerfile +++ b/ImageApi/Dockerfile @@ -1,15 +1,13 @@ -#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed. -#For more information, please see https://aka.ms/containercompat - -FROM microsoft/dotnet:2.2-aspnetcore-runtime-nanoserver-1803 AS base +FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-sdk-nanoserver-1803 AS build +FROM microsoft/dotnet:2.2-sdk AS build WORKDIR /src COPY ["ImageApi/ImageApi.csproj", "ImageApi/"] COPY ["ImageApi.Core/ImageApi.Core.csproj", "ImageApi.Core/"] COPY ["ImageCore/ImageCore.csproj", "ImageCore/"] +COPY ["Persistence/ImageCore.Persistence.EntityFramework/ImageCore.Persistence.EntityFramework.csproj", "Persistence/ImageCore.Persistence.EntityFramework/"] RUN dotnet restore "ImageApi/ImageApi.csproj" COPY . . WORKDIR "/src/ImageApi" diff --git a/ImageApi/Dockerfile1.original b/ImageApi/Dockerfile1.original new file mode 100644 index 0000000..c52689d --- /dev/null +++ b/ImageApi/Dockerfile1.original @@ -0,0 +1,24 @@ +#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed. +#For more information, please see https://aka.ms/containercompat + +FROM microsoft/dotnet:2.2-aspnetcore-runtime-nanoserver-1803 AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.2-sdk-nanoserver-1803 AS build +WORKDIR /src +COPY ["ImageApi/ImageApi.csproj", "ImageApi/"] +COPY ["ImageApi.Core/ImageApi.Core.csproj", "ImageApi.Core/"] +COPY ["ImageCore/ImageCore.csproj", "ImageCore/"] +RUN dotnet restore "ImageApi/ImageApi.csproj" +COPY . . +WORKDIR "/src/ImageApi" +RUN dotnet build "ImageApi.csproj" -c Release -o /app + +FROM build AS publish +RUN dotnet publish "ImageApi.csproj" -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "ImageApi.dll"] \ No newline at end of file diff --git a/ImageApi/ImageApi.csproj b/ImageApi/ImageApi.csproj index bf2c7f6..f3bc101 100644 --- a/ImageApi/ImageApi.csproj +++ b/ImageApi/ImageApi.csproj @@ -3,7 +3,7 @@ netcoreapp2.2 InProcess - Windows + Linux @@ -12,9 +12,12 @@ + + + diff --git a/ImageApi/ImageFileInfoExtensions.cs b/ImageApi/ImageFileInfoExtensions.cs index 9136e2c..b5c4a14 100644 --- a/ImageApi/ImageFileInfoExtensions.cs +++ b/ImageApi/ImageFileInfoExtensions.cs @@ -10,9 +10,10 @@ public static class ImageFileInfoExtensions public static ImageDto ToImage(this ImageFileInfo fileInfo) { ImageFormat imageFormat; - switch (fileInfo.Extension) + switch (fileInfo.Extension.ToLower()) { case ".jpg": + case ".jpeg": imageFormat = ImageFormat.Jpeg; break; case ".gif": diff --git a/ImageApi/Program.cs b/ImageApi/Program.cs index a743a1f..65862ca 100644 --- a/ImageApi/Program.cs +++ b/ImageApi/Program.cs @@ -19,6 +19,8 @@ public static void Main(string[] args) public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseUrls("http://*:8089") .UseStartup(); } } diff --git a/ImageApi/Properties/PublishProfiles/FolderProfile.pubxml b/ImageApi/Properties/PublishProfiles/FolderProfile.pubxml index 2500033..031a5e7 100644 --- a/ImageApi/Properties/PublishProfiles/FolderProfile.pubxml +++ b/ImageApi/Properties/PublishProfiles/FolderProfile.pubxml @@ -15,5 +15,8 @@ 2528c869-0822-49b2-ba00-6beebc316244 D:\wwwroot\ImageService False + netcoreapp2.2 + false + <_IsPortable>true \ No newline at end of file diff --git a/ImageApi/Properties/PublishProfiles/FolderProfile1.pubxml b/ImageApi/Properties/PublishProfiles/FolderProfile1.pubxml new file mode 100644 index 0000000..444f409 --- /dev/null +++ b/ImageApi/Properties/PublishProfiles/FolderProfile1.pubxml @@ -0,0 +1,22 @@ + + + + + FileSystem + FileSystem + Release + Any CPU + + True + False + netcoreapp2.2 + 2528c869-0822-49b2-ba00-6beebc316244 + false + <_IsPortable>true + D:\wwwroot\ImageService_Demo + False + + \ No newline at end of file diff --git a/ImageApi/Properties/PublishProfiles/FolderProfile2.pubxml b/ImageApi/Properties/PublishProfiles/FolderProfile2.pubxml new file mode 100644 index 0000000..8b70de7 --- /dev/null +++ b/ImageApi/Properties/PublishProfiles/FolderProfile2.pubxml @@ -0,0 +1,19 @@ + + + + + FileSystem + FileSystem + Release + Any CPU + + True + False + 2528c869-0822-49b2-ba00-6beebc316244 + D:\wwwroot\ImageServer_Mysql + False + + \ No newline at end of file diff --git a/ImageApi/Startup.cs b/ImageApi/Startup.cs index fe2eacd..7c20f5d 100644 --- a/ImageApi/Startup.cs +++ b/ImageApi/Startup.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -22,13 +23,13 @@ public class Startup { private const string DefaultCorsPolicyName = "public"; - private readonly IConfigurationRoot _appConfiguration; + private readonly IConfiguration configuration; private readonly IHostingEnvironment _hostingEnvironment; - public Startup(IHostingEnvironment env) + public Startup(IHostingEnvironment env, IConfiguration configuration) { _hostingEnvironment = env; - _appConfiguration = env.GetAppConfiguration(); + this.configuration = configuration; } // This method gets called by the runtime. Use this method to add services to the container. @@ -39,13 +40,30 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton( new PhysicalFileProvider(Directory.GetCurrentDirectory())); services.AddSingleton(); + services.AddEntityFrameworkNpgsql(); + services.AddAuthentication("Bearer") + .AddIdentityServerAuthentication(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.ApiName = configuration["AuthServer:ApiName"]; + //options.InboundJwtClaimTypeMap["sub"] = AbpClaimTypes.UserId; + //options.InboundJwtClaimTypeMap["role"] = AbpClaimTypes.Role; + //options.InboundJwtClaimTypeMap["email"] = AbpClaimTypes.Email; + //options.InboundJwtClaimTypeMap["email_verified"] = AbpClaimTypes.EmailVerified; + //options.InboundJwtClaimTypeMap["phone_number"] = AbpClaimTypes.PhoneNumber; + //options.InboundJwtClaimTypeMap["phone_number_verified"] = AbpClaimTypes.PhoneNumberVerified; + //options.InboundJwtClaimTypeMap["name"] = AbpClaimTypes.UserName; + }); services.AddImageService(option => { - option.Filters = new[] {".jpg", ".png", ".bmp"}; + option.Filters = new[] {".jpg", ".jpeg", ".gif", ".png", ".bmp"}; }) .UseEntityFrameworkStore(builder => { - builder.UseSqlServer("Server=localhost; Database=ImageDb; Trusted_Connection=True;"); + //builder.UseSqlServer("Server=localhost; Database=ImageDb; Trusted_Connection=True;"); + builder.UseNpgsql(configuration.GetConnectionString("Default")); + //builder.UseMySql(configuration.GetConnectionString("Mysql")); }); services.AddCors(options => { @@ -53,7 +71,7 @@ public void ConfigureServices(IServiceCollection services) { builder .WithOrigins( - _appConfiguration["CorsOrigins"] + configuration["CorsOrigins"] .Split(",", StringSplitOptions.RemoveEmptyEntries) .ToArray() ) @@ -68,6 +86,15 @@ public void ConfigureServices(IServiceCollection services) c.SwaggerDoc("v1", new Info {Title = "图片资源API", Version = "v1"}); c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "ImageApi.xml")); c.OperationFilter(); + c.AddSecurityDefinition("bearerAuth", new ApiKeyScheme() + { + Description = "JWT Authorization header using the bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = "header", + Type = "apiKey" + }); + var security = new Dictionary> { { "bearerAuth", new string[] { } }, }; + c.AddSecurityRequirement(security); }); } @@ -79,17 +106,10 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseDeveloperExceptionPage(); } app.UseCors(DefaultCorsPolicyName); + app.UseAuthentication(); app.UseMvcWithDefaultRoute(); app.UseSwagger() .UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "图片资源API"); }); } } - - public static class HostingEnvironmentExtensions - { - public static IConfigurationRoot GetAppConfiguration(this IHostingEnvironment env) - { - return AppConfigurations.Get(env.ContentRootPath, env.EnvironmentName); - } - } } diff --git a/ImageApi/SwaggerFileUploadFilter.cs b/ImageApi/SwaggerFileUploadFilter.cs index c287c39..90006cb 100644 --- a/ImageApi/SwaggerFileUploadFilter.cs +++ b/ImageApi/SwaggerFileUploadFilter.cs @@ -20,9 +20,10 @@ public void Apply(Operation operation, OperationFilterContext context) } var fileParameters = context.ApiDescription.ActionDescriptor.Parameters - .Where(n => n.ParameterType == typeof(IFormFile)||n.BindingInfo.BindingSource == BindingSource.FormFile).ToList(); + .Where(n => n.ParameterType == typeof(IFormFile) || + (n.BindingInfo != null && n.BindingInfo.BindingSource == BindingSource.FormFile)).ToList(); - if (fileParameters.Count < 0) + if (fileParameters.Count == 0) { return; } diff --git a/ImageApi/appsettings.json b/ImageApi/appsettings.json index 9c898f8..3d03031 100644 --- a/ImageApi/appsettings.json +++ b/ImageApi/appsettings.json @@ -1,4 +1,11 @@ { + "ConnectionStrings": { + "Default": "host=6.6.6.6;port=5432;database=image;userid=postgres;pwd=123123;" + }, + "AuthServer": { + "Authority": "http://localhost:44305/", + "ApiName": "Image" + }, "Logging": { "LogLevel": { "Default": "Warning" diff --git a/ImageCore/ImageOption.cs b/ImageCore/ImageOption.cs index 3265512..1d4d3aa 100644 --- a/ImageCore/ImageOption.cs +++ b/ImageCore/ImageOption.cs @@ -12,9 +12,9 @@ public class ImageOption public string[] Filters { get; set; } = {".jpg", ".png", ".bmp"}; /// - /// 可接收文件最大的大小单位kb,默认为2MB + /// 可接收文件最大的大小单位kb,默认为5MB /// - public long MaxContentLength { get; set; } = 1024*1024 * 2; + public long MaxContentLength { get; set; } = 1024*1024 * 5; /// /// 文件存储模式,默认为分布式存储 diff --git a/Persistence/ImageCore.Persistence.EntityFramework/ImageCore.Persistence.EntityFramework.csproj b/Persistence/ImageCore.Persistence.EntityFramework/ImageCore.Persistence.EntityFramework.csproj index 12b9d5b..63b61f7 100644 --- a/Persistence/ImageCore.Persistence.EntityFramework/ImageCore.Persistence.EntityFramework.csproj +++ b/Persistence/ImageCore.Persistence.EntityFramework/ImageCore.Persistence.EntityFramework.csproj @@ -8,6 +8,8 @@ + + diff --git a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.Designer.cs b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.Designer.cs similarity index 83% rename from Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.Designer.cs rename to Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.Designer.cs index f011f2b..ae6af73 100644 --- a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.Designer.cs +++ b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.Designer.cs @@ -3,23 +3,23 @@ using ImageCore.Persistence.EntityFramework; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace ImageCore.Persistence.EntityFramework.Migrations { [DbContext(typeof(ImageDbContext))] - [Migration("20190301113830_Inital")] - partial class Inital + [Migration("20190426035517_initial")] + partial class initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) .HasAnnotation("ProductVersion", "2.2.2-servicing-10034") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("ImageCore.Persistence.EntityFramework.Image", b => { @@ -52,8 +52,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id"); b.HasIndex("Key") - .IsUnique() - .HasFilter("[Key] IS NOT NULL"); + .IsUnique(); b.ToTable("Images"); }); diff --git a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.cs b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.cs similarity index 92% rename from Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.cs rename to Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.cs index 109f587..48f532d 100644 --- a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190301113830_Inital.cs +++ b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/20190426035517_initial.cs @@ -3,7 +3,7 @@ namespace ImageCore.Persistence.EntityFramework.Migrations { - public partial class Inital : Migration + public partial class initial : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -32,8 +32,7 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_Images_Key", table: "Images", column: "Key", - unique: true, - filter: "[Key] IS NOT NULL"); + unique: true); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/ImageDbContextModelSnapshot.cs b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/ImageDbContextModelSnapshot.cs index 19693ae..3dfff7c 100644 --- a/Persistence/ImageCore.Persistence.EntityFramework/Migrations/ImageDbContextModelSnapshot.cs +++ b/Persistence/ImageCore.Persistence.EntityFramework/Migrations/ImageDbContextModelSnapshot.cs @@ -3,8 +3,8 @@ using ImageCore.Persistence.EntityFramework; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace ImageCore.Persistence.EntityFramework.Migrations { @@ -15,9 +15,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) .HasAnnotation("ProductVersion", "2.2.2-servicing-10034") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("ImageCore.Persistence.EntityFramework.Image", b => { @@ -50,8 +50,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); b.HasIndex("Key") - .IsUnique() - .HasFilter("[Key] IS NOT NULL"); + .IsUnique(); b.ToTable("Images"); });