Unity上传文件到.net core 服务器并提供二维码下载

距离上一次分享,感觉过去了好久好久。最近手里的项目也完成的差不多了,接下来的几天(年前)会和大家分享一下在项目中收获的东西。

需求:用户使用App之后,可以保存当前屏幕截图,然后通过扫描二维码的形式下载图片到手机上。

环境:
  • 前台:Unity
  • 后台:asp.net core webapi
后台搭建

.net core 环境安装啥的我就不说了,去微软官网下载对应的SDK安装好。我使用的是.net core3.1,开发工具是VSCode。

创建工程

打开终端,输入 : dotnet new webapi

终端自动创建一个webpai的模板,我们对这个模板做一些修改(模板自带了一个天气查询的例子)。

找到 Controller/WeatherForecastController.cs,修改代码如下(具体请认真看代码注释代码注释):
using System.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;

namespace FileUpload.Controllers
{
    /// <summary>
    /// 访问当前控制器使用的路由地址
    /// http://localhost/api/img
    /// </summary>
    [Route("api/img")]
    public class WeatherForecastController : Controller
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;
        /// <summary>
        /// 使用.net core 的依赖注入,将要用到的实例注入进来
        /// </summary>
        private readonly IWebHostEnvironment _environment;

        public WeatherForecastController(ILogger<WeatherForecastController> logger,
        IWebHostEnvironment environment)
        {
            _logger = logger;
            /// <summary>
            /// 使用.net core 的依赖注入,将要用到的实例注入进来
            /// </summary>
            _environment = environment ?? throw new ArgumentNullException(nameof(environment));
        }

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
        /// <summary>
        /// 重要提示,函数的参数的名字必须保持和前端传来的字段名字相同
        /// 在发布窗体数据或直接使用 JavaScript FormData 的非 Razor 窗体中,
        /// 窗体元素或 FormData 指定的名称必须与控制器操作的参数名称相匹配。
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<string> UploadImage(IFormFile file)
        {
            /// <summary>
            /// 获取当前静态文件目录所在文件夹
            /// </summary>
            /// <returns></returns>
            var uploads = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
            /// <summary>
            /// 按年月日时分秒的格式保存当前文件
            /// </summary>
            /// <returns></returns>
            var fileName = DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".jpg";

            var targetPath = Path.Combine(uploads, fileName);
            using (var steam = new FileStream(targetPath, FileMode.Create))
            {
                /// <summary>
                /// 将获取到的文件写入到服务器的指定目录中
                /// </summary>
                /// <returns></returns>
                await file.CopyToAsync(steam);
                await steam.FlushAsync();
                steam.Close();
                /// <summary>
                /// 将当前接收到的文件的名字返回给前端
                /// </summary>
                return fileName;
            }
        }
    }
}
修改Startup.cs如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace FileUpload
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

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

            //services.AddAuthentication(CertificateAuthenticationDefaults.)
        }

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

            app.UseHttpsRedirection();
            ///添加访问静态文件支持
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

修改Properties/launchSettings.json文件,在本机测试时不使用https协议,不然访问不到,等将代码上传到服务器之后才改回来即可。
{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:23643",
      "sslPort": 44395
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "FileUpload": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000;",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

到这里后台代码搭建就完成了,如果你出了什么问题,请看代码注释。其它具体的细节请看微软官方文档。

前台搭建

前台当然是在我们熟悉的Unity中完成啦。

获取当前屏幕截图
public void StartScreenShoot()
    {
        StartCoroutine(ScreenSHOT());
    }
private IEnumerator ScreenShot()
    {
        yield return new WaitForEndOfFrame();
        //设置要截取的图片为矩形,大小和屏幕等高
        Texture2D screenShot = new Texture2D(Screen.height, Screen.height, TextureFormat.RGB24, false);
        var horizontalOffset = (Screen.width - Screen.height) / 2f;
        //读取屏幕中间部分的像素,高度和当前屏幕高度等高
        screenShot.ReadPixels(new Rect(horizontalOffset, 0, Screen.height, Screen.height), 0, 0);
        screenShot.Apply();
        yield return new WaitForEndOfFrame();
    }
将当前屏幕截图上传到服务器(请仔细看代码注释)

本例子中实现的是,将图片上传到服务器,服务器返回当前上传图片的文件名,用于网址访问

  /// <summary>
    /// 将图片上传到自家服务器
    /// </summary>
    /// <param name="targetTexture"></param>
    /// <returns></returns>
    IEnumerator UploadTexture(Texture2D targetTexture)
    {
        /// <summary>
        /// 将屏幕截图编码为字节流
        /// </summary>
        /// <returns></returns>
        byte[] bytes = targetTexture.EncodeToJPG();
        /// <summary>
        /// 构建传输表单
        /// </summary>
        /// <returns></returns>
        WWWForm wwwForm = new WWWForm();
        /// <summary>
        /// 重要:构建表单数据,这里的file字段必须保持和服务器对应函数的参数名称相同
        /// </summary>
        /// <returns></returns> 
        wwwForm.AddBinaryData("file", bytes, "myfile.jpg");
        /// <summary>
        /// 构建POST请求,打包时将下面网址替换为自己服务器的网址,也就是服务器对应的请求路由
        /// </summary>
        /// <returns></returns>
        using (UnityWebRequest www = UnityWebRequest.Post("http://localhost:5000/api/img", wwwForm))
        {
            /// <summary>
            /// 声明一个内存缓冲,用于接收服务器传回的内容
            /// </summary>
            /// <returns></returns>
            www.downloadHandler = new DownloadHandlerBuffer();
            /// <summary>
            /// 重要:指定当前传输的文件类型,如果不指定,服务器可能会识别不了
            /// </summary>
            /// <returns></returns>
            www.uploadHandler.contentType = "multipart/form-data";

            yield return www.SendWebRequest();

            if (www.isHttpError || www.isNetworkError)
            {
                Debug.Log("自己的服务器错误");
                loading.gameObject.SetActive(true);
            }
            else
            {
                //将接受到的图片在服务器的文件名和你自己的服务器域名组合起来
                var imagURL = "http://localhost:5000/" + www.downloadHandler.text;
                Debug.Log(imagURL);
                yield return StartCoroutine(GetCLQRCode(imagURL));
            }
        }
    }
将上一步中获取的图片网址编码为一个二维码,用户扫描二维码即可访问图片

生成二维码有很多种方式,这里使用最简单的一种。我们直接调用草料二维码提供的API接口来生成二维码。参考文章(https://www.jianshu.com/p/fece59a67860),看代码注释:

  /// <summary>
    /// 从二维码提供商那里下载被转码后的二维码
    /// </summary>
    /// <param name="qrcodeImage"></param>
    /// <returns></returns>
    IEnumerator GetCLQRCode(string qrcodeImage)
    {
        UnityWebRequest www = new UnityWebRequest();
        //构建GET请求,将需要编码的字符串包装到请求参数中
        string url = "https://cli.im/api/qrcode/code?text=" + qrcodeImage + "&mhid=t0uVWg+9ks4hMHcmLtBTOaM";
        www.url = url;


        Debug.Log(url);
        www.method = UnityWebRequest.kHttpVerbGET;
        www.downloadHandler = new DownloadHandlerBuffer();

        yield return www.SendWebRequest();

        if (www.isHttpError || www.isNetworkError)
        {
            Debug.Log("二维码服务商错误");
        }
        else
        {
            /// <summary>
            /// 请求成功,草料API会返回一个html格式的网页,
            /// 需要对网页做一些内容截取,获取到返回的二维码的具体地址
            /// </summary>
            var w = www.downloadHandler.text;
            Debug.Log(w);
            string s = w.Substring(w.IndexOf("<img src=") + 12, w.Length - (w.IndexOf("<img src=") + 12));

            string result = s.Substring(0, s.IndexOf("\""));

            Debug.Log(result);

            UnityWebRequest qrcode = new UnityWebRequest();

            qrcode.url = result;
            qrcode.method = UnityWebRequest.kHttpVerbGET;
            qrcode.downloadHandler = new DownloadHandlerBuffer();

            yield return qrcode.SendWebRequest();

            if (qrcode.isHttpError || qrcode.isNetworkError)
            {
                Debug.Log(qrcode.error);
                Debug.Log("访问二维码出错");
            }
            else
            {
                //构建一个texture2D用来存储二维码
                QRCodeTexture2D = new Texture2D(400, 400);
                
                yield return  new WaitForSeconds(3f);
                //将接收到的图片数据写入构建的Texture2D中
                QRCodeTexture2D.LoadImage(qrcode.downloadHandler.data);
                QRCodeTexture2D.Apply();

                rawImage.gameObject.SetActive(true);
                //声明一个rawimage用来显示二维码
                rawImage.texture = QRCodeTexture2D;
            }

            Debug.Log("获取二维码成功");
        }
    }

完。

更多内容,欢迎关注:


在这里插入图片描述

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值