65.9K
CodeProject 正在变化。 阅读更多。
Home

上传图片到 .NET Core 2.1 API

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (18投票s)

2018年8月15日

CPOL

3分钟阅读

viewsIcon

74697

downloadIcon

1030

本文介绍如何从您的 .NET core 2.1 API 上传和检索图像。

引言

本文介绍如何从您的 .NET Core 2.1 API 上传和检索图像。可以使用在浏览器上显示的图像名称来访问图像。

我还在 Github 上传了代码。希望您觉得本指南有用。

背景

  • 我使用 Postman 发送 POST 请求,任何替代方案都应该可以。
  • .NET Core 2.1 兼容的 Visual Studio
  • 了解依赖注入
  • Task<>async 方法有良好的了解
  • 对 API 控制器有一些了解(我从很高的层面解释了,这足以理解发生了什么)

代码

首先,让我们创建一个新的 ASP .NET Core Web 应用程序并选择 API 项目,并将其命名为 ImageUploader

项目结构

以下是我用来创建项目的项目结构。我将解释每个文件的作用

Handler 文件夹有一个 ImageHandler.csImageController 使用它,通过将处理程序注入到控制器中来调用程序的图像写入组件。

ImageWriter 是一个 .NET Core 2.1 类库项目,负责将图像写入磁盘。我们将图像写入我创建的 wwwroot/images 文件夹中。

WriterHelper.csImageWriter 使用的辅助类,用于验证给定文件是否为图像文件。

代码

我想从下面的 WriterHelper.cs 开始,这是验证上传文件的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ImageWriter.Helper
{
    public class WriterHelper
    {
        public enum ImageFormat
        {
            bmp,
            jpeg,
            gif,
            tiff,
            png,
            unknown
        }

        public static ImageFormat GetImageFormat(byte[] bytes)
        {
            var bmp = Encoding.ASCII.GetBytes("BM");     // BMP
            var gif = Encoding.ASCII.GetBytes("GIF");    // GIF
            var png = new byte[] { 137, 80, 78, 71 };              // PNG
            var tiff = new byte[] { 73, 73, 42 };                  // TIFF
            var tiff2 = new byte[] { 77, 77, 42 };                 // TIFF
            var jpeg = new byte[] { 255, 216, 255, 224 };          // jpeg
            var jpeg2 = new byte[] { 255, 216, 255, 225 };         // jpeg canon

            if (bmp.SequenceEqual(bytes.Take(bmp.Length)))
                return ImageFormat.bmp;

            if (gif.SequenceEqual(bytes.Take(gif.Length)))
                return ImageFormat.gif;

            if (png.SequenceEqual(bytes.Take(png.Length)))
                return ImageFormat.png;

            if (tiff.SequenceEqual(bytes.Take(tiff.Length)))
                return ImageFormat.tiff;

            if (tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
                return ImageFormat.tiff;

            if (jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
                return ImageFormat.jpeg;

            if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
                return ImageFormat.jpeg;

            return ImageFormat.unknown;
        }
    }
}

现在,是时候设置实际的 ImageWriter.cs 了,但在那之前,让我们填充 IImageWriter.cs 接口,以便我们可以在处理程序中注入它。

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace ImageWriter.Interface
{
    public interface IImageWriter
    {
        Task<string> UploadImage(IFormFile file);
    }
}

现在是 ImageWriter.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using ImageWriter.Helper;
using ImageWriter.Interface;
using Microsoft.AspNetCore.Http;

namespace ImageWriter.Classes
{
    public class ImageWriter : IImageWriter
    {
        public async Task<string> UploadImage(IFormFile file)
        {
            if (CheckIfImageFile(file))
            {
                return await WriteFile(file);
            }

            return "Invalid image file";
        }

        /// <summary>
        /// Method to check if file is image file
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private bool CheckIfImageFile(IFormFile file)
        {
            byte[] fileBytes;
            using (var ms = new MemoryStream())
            {
                file.CopyTo(ms);
                fileBytes = ms.ToArray();
            }

            return WriterHelper.GetImageFormat(fileBytes) != WriterHelper.ImageFormat.unknown;
        }

        /// <summary>
        /// Method to write file onto the disk
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<string> WriteFile(IFormFile file)
        {
            string fileName;
            try
            {
                var extension = "." + file.FileName.Split('.')[file.FileName.Split('.').Length - 1];
                fileName = Guid.NewGuid().ToString() + extension; //Create a new Name 
                                                              //for the file due to security reasons.
                var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\images", fileName);

                using (var bits = new FileStream(path, FileMode.Create))
                {
                    await file.CopyToAsync(bits);
                }
            }
            catch (Exception e)
            {
                return e.Message;
            }

            return fileName;
        }
    }
}

这里的主要逻辑是首先检查文件。如果它是图像文件,我们出于安全原因使用新名称写入它,并将名称返回给用户(我们也可以使用持久性存储将文件名映射到新名称,以便稍后检索)。如果文件无效,我们会返回一条错误消息。

完成主要功能后,让我们转到处理程序,在那里我们将传入的请求定向到适当的方法。

using System.Threading.Tasks;
using ImageWriter.Interface;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace ImageUploader.Handler
{
    public interface IImageHandler
    {
        Task<IActionResult> UploadImage(IFormFile file);
    }

    public class ImageHandler : IImageHandler
    {
        private readonly IImageWriter _imageWriter;
        public ImageHandler(IImageWriter imageWriter)
        {
            _imageWriter = imageWriter;
        }

        public async Task<IActionResult> UploadImage(IFormFile file)
        {
            var result = await _imageWriter.UploadImage(file);
            return new ObjectResult(result);
        }
    }
}

在这里,ImageWriter 被注入到处理程序中。

是时候配置控制器了

using System.Threading.Tasks;
using ImageUploader.Handler;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace ImageUploader.Controllers
{
    [Route("api/image")]
    public class ImagesController : Controller
    {
        private readonly IImageHandler _imageHandler;

        public ImagesController(IImageHandler imageHandler)
        {
            _imageHandler = imageHandler;
        }

        /// <summary>
        /// Uplaods an image to the server.
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<IActionResult> UploadImage(IFormFile file)
        {
            return await _imageHandler.UploadImage(file);
        }
    }
}

最后,我们必须配置 Startup.cs 以通知应用程序使用 wwwroot/images 文件夹。我们可以通过在我们的 Configure 方法中添加一行简单的代码来实现这一点。

我们还可以通过指定上面屏幕截图注释区域内的路径来引用 wwwroot 文件夹之外的文件。在上面的屏幕截图中,注释的配置将使用主项目中的名为 StaticFiles 的文件夹。

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            //Use this to set path of files outside the wwwroot folder
            //app.UseStaticFiles(new StaticFileOptions
            //{
            //    FileProvider = new PhysicalFileProvider(
            //        Path.Combine(Directory.GetCurrentDirectory(), "StaticFiles")),
            //    RequestPath = "/StaticFiles"
            //});

            app.UseStaticFiles(); //letting the application know that we need access to wwwroot folder.

            app.UseHttpsRedirection();
            app.UseMvc();
        }

最后,我们在 ConfigureServices 方法中为 ImageHandler ImageWriter 添加依赖注入。

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddTransient<IImageHandler, ImageHandler>();
            services.AddTransient<ImageWriter.Interface.IImageWriter, 
                                  ImageWriter.Classes.ImageWriter>();
        }

结果

让我们启动它并使用 postman 发送一个图像文件

确保将 key 设置为 file,将 value 设置为有效的图像文件。还要确保请求是 POST 请求。正如我们从上面的屏幕截图中所看到的,我们得到了上传文件的名称。让我们检查它是否确实在 images 文件夹中

现在我们如何检索它?这非常简单,我们所要做的就是调用 http://ipaddress:port/images/filename

就是这样!一个用于上传和检索图像的 API。您可以使用上面的链接将图像直接嵌入到 HTML <img src =””> 标签中。

© . All rights reserved.