如何在前端html上传文件到服务器处理并反馈给前端展示

一种前端post到后端处理并反馈给前端展示的解决方案 仓库地址在评论区

  • html页面接受文件拖拽
  • 利于时间戳的实时性 和 PI的不确定性 进行验证
  • FormData
  • ajax上传文件到后台
  • C# http 服务后端
    • SimpleHttp:基于HttpListener二次封装
    • Zip Tool
    • Base64工具类封装
    • 基于PI的时钟加密key
  • 仓库地址

html页面接受文件拖拽

enableDropEvent: function (dropHandler, node = null) {
            var el = node || document;
            el.addEventListener('dragleave', e => {
                e.preventDefault();
            }
            );
            el.addEventListener('dragover', e => {
                e.preventDefault();
            }
            );
            el.addEventListener('dragenter', e => {
                e.preventDefault();
            }
            );
            el.addEventListener('drop', e => {
                e.preventDefault();
                let files = e.dataTransfer.files;
                dropHandler && dropHandler(files);
            });
        }

利于时间戳的实时性 和 PI的不确定性 进行验证

在将待处理文件POST到后端服务器时 我们需要同时传入 时间戳 和 8位secretKey
后端在处理的时候 需要考虑 网络的延迟等情况
所以后端需要对 传入的时间戳 和 当前时间进行 2秒左右的 误差判定 具体时长按项目判定 如果你上传处理的文件较大 那这个时间就需要被加长

通过WebAssembly隐藏关键代码
https://blog.csdn.net/qq_39162566/article/details/121425016
webassembly studio 在线编辑器

SercertUtility.wasm 源码
由于 跨平台每个系统之间 PI的精度是存在误差的 这里我固定选取了 PI后10000~10180位作为种子
根据自己项目的需要 可以选择随意 PI后指定位置 指定长度的种子
我这里就用180位作为测试了 然后secert key长度选定的是8位 你也可以选择32位 随君所愿

#include
#define WASM_EXPORT __attribute__((visibility("default")))

WASM_EXPORT
unsigned long GetSecret(unsigned long ts) {
    static int pi10000[] = { 3,0,1,3,0,5,2,7,9,3,2,0,5,4,2,7,4,6,2,8,6,5,4,0,3,6,0,3,6,7,4,5,3,2,8,6,5,1,0,5,7,0,6,5,8,7,4,8,8,2,2,5,6,9,8,1,5,7,9,3,6,7,8,9,7,6,6,9,7,4,2,2,0,5,7,5,0,5,9,6,8,3,4,4,0,8,6,9,7,3,5,0,2,0,1,4,1,0,2,0,6,7,2,3,5,8,5,0,2,0,0,7,2,4,5,2,2,5,6,3,2,6,5,1,3,4,1,0,5,5,9,2,4,0,1,9,0,2,7,4,2,1,6,2,4,8,4,3,9,1,4,0,3,5,9,9,8,9,5,3,5,3,9,4,5,9,0,9,4,4,0,7,0,4,6,9,1,2,0,9 };
    int arrLen = sizeof(pi10000) / sizeof(pi10000[0]);

    unsigned long ID = 0;
    for (int i = 0; i < 8; i++)
    {
        int index = fmod(ts, pow(10, i + 1));
        index %= arrLen;
        ID += pi10000[index] * pow(10, i);
    }
    
    return ID;
}

fetch 加载.wasm文件

fetch('../SercertUtility.wasm').then(response =>
        response.arrayBuffer()
    ).then(bytes => WebAssembly.instantiate(bytes)).then(lib => {
        const GetSecret = lib.instance.exports.GetSecret;
        //将加载的函数指针存于 一个全局的对象中  方便调用 您也用可以使用 window.GetSecret 来缓存
        self.jcUtils.GetSecret = () => {
            let ts = Date.now();
            let secret = GetSecret(ts);
            return [ts, secret];
        }
    }).catch(console.error);

FormData

  var formData = new FormData();
  formData.append('file', dropFile);
  let data = jcUtils.GetSecret(); //jcUtils 是我定义的一个工具对象  里面包含了一些工具方法 其中 就包含 加载 .wasm文件 并缓存内部方法的实现
  formData.append('ts', data[0]);
  formData.append('secret', data[1]);

ajax上传文件到后台

$.ajax({
    url: 'http://www.geek7.ltd:8081/BuildPlayable/',
    type: 'POST',
    data: formData,
    mimeType: "multipart/form-data",
    cache: false,
    processData: false,
    contentType: false,
    responseType: 'arraybuffer',
    success: function (data) {

      try {

        let r = JSON.parse(data);
        if (r.succeed) {
          
          let url = r.url;
          (function (url) {
            let name = url.substring(url.lastIndexOf("/") + 1);
            let objectURL = window.URL.createObjectURL(new Blob([url]));
            let a = document.createElement('a')
            a.href = objectURL
            a.download = name
            a.click()
            a.remove()
          })(url)

        }

      }
      catch (e) {
        console.log(e);
      }
    },
  });

C# http 服务后端

SimpleHttp:基于HttpListener二次封装

    class SimpleHttp : IDisposable
    {
        private readonly HttpListener _listener;                        // HTTP 协议侦听器
        private readonly Thread _listenerThread;                        // 监听线程
        private readonly Thread[] _workers;                             // 工作线程组
        private readonly ManualResetEvent _stop, _ready;                // 通知停止、就绪
        private Queue<HttpListenerContext> _queue;                      // 请求队列
        //POST方法字典
        Dictionary<string, RouteInfo> _route_pool = new Dictionary<string, RouteInfo>();


        //构造 ( 最大多线程数 )
        public SimpleHttp(int maxThreads)
        {
            _workers = new Thread[maxThreads];
            _queue = new Queue<HttpListenerContext>();
            _stop = new ManualResetEvent(false);
            _ready = new ManualResetEvent(false);
            _listener = new HttpListener();
            _listenerThread = new Thread(HandleRequests);
        }

        //启动Http服务器
        public void Start(int port)
        {

            // 启动Http服务
            _listener.Prefixes.Add(String.Format("http://*:{0}/", port));
            _listener.Start();
            _listenerThread.Start();

            // 启动工作线程
            for (int i = 0; i < _workers.Length; i++)
            {
                _workers[i] = new Thread(Worker);
                _workers[i].Start();
            }
        }

        //响应
        public void Response(HttpListenerContext ctx, string res, int state = 200)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(res);
            Response(ctx, buffer, state);
        }

        //响应
        public void Response(HttpListenerContext ctx, byte[] buffer, int state = 200)
        {
            //返回信息
            ctx.Response.StatusCode = state;
            ctx.Response.Headers.Add("Access-Control-Allow-Origin", "*");//跨域
            ctx.Response.ContentLength64 = buffer.Length;
            ctx.Response.OutputStream.Write(buffer, 0, buffer.Length);
            ctx.Response.OutputStream.Close();
            ctx.Response.Close();
        }

        //添加处理方法  
        public void AddRouter(string method, Action<HttpListenerContext, string, HttpMultipartParser> handler, string fileKey = "file")
        {
            _route_pool.Add(method, new RouteInfo()
            {
                _handFunc = handler,
                _fileKey = fileKey
            });
        }

        // 释放资源
        public void Dispose()
        {
            Stop();
        }

        // 停止服务
        public void Stop()
        {
            _stop.Set();
            _listenerThread.Join();
            foreach (Thread worker in _workers)
            {
                worker.Join();
            }
            _listener.Stop();
        }

        // 处理请求
        private void HandleRequests()
        {
            while (_listener.IsListening)
            {
                var context = _listener.BeginGetContext(ContextReady, null);
                if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
                {
                    return;
                }
            }
        }

        // 请求就绪加入队列
        private void ContextReady(IAsyncResult ar)
        {
            try
            {
                lock (_queue)
                {
                    _queue.Enqueue(_listener.EndGetContext(ar));
                    _ready.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(string.Format("[HttpServerBase::ContextReady]err:{0}", e.Message));
            }
        }

        // 处理一个任务
        private void Worker()
        {
            WaitHandle[] wait = new[] { _ready, _stop };
            while (0 == WaitHandle.WaitAny(wait))
            {
                HttpListenerContext context;
                lock (_queue)
                {
                    if (_queue.Count > 0)
                        context = _queue.Dequeue();
                    else
                    {
                        _ready.Reset();
                        continue;
                    }
                }

                try
                {
                    ProcessHttpRequest(context);
                }
                catch (Exception e)
                {
                    Console.WriteLine(string.Format("[HttpServerBase::Worker]err:{0}", e.Message));
                    Error(context); //返回前端消息 防止前端锁死
                }
            }
        }

        // 请求处理函数
        protected void ProcessHttpRequest(HttpListenerContext ctx)
        {
            var url = ctx.Request.RawUrl.ToString();

            HttpListenerRequest request = ctx.Request;
            HttpListenerResponse response = ctx.Response;

            if (request.HttpMethod == "POST")
            {
                //获取调用方法名
                int index = url.IndexOf('/', 1);

                if (index != -1)
                {
                    string method = url.Substring(1, index - 1);
                    RouteInfo router = null;

                    //获取函数指针
                    if (!_route_pool.TryGetValue(method, out router))
                    {
                        Error(ctx);
                        return;
                    }

                    //获取Body
                    var hmp = new HttpMultipartParser(request.InputStream, router._fileKey);

                    //获取参数
                    string param = index + 1 < url.Length ? url.Substring(index + 1, url.Length - 1 - index) : null;

                    //处理
                    router._handFunc(ctx, param, hmp);
                }
                else
                {
                    Error(ctx, $"post error!!! example:\n\thttp://{ctx.Request.UserHostAddress}/hello/");
                }
            }
            else
            {
                Error(ctx, "Only the POST method is supported !");
            }

        }

        //默认错误处理
        public void Error(HttpListenerContext context, string errContent = "Not Found Method!")
        {
            Response(context, errContent, 444);
        }
    }


    /// 
    /// 路由信息
    /// 
    internal class RouteInfo
    {
        //处理方法
        public Action<HttpListenerContext, string, HttpMultipartParser> _handFunc = null;
        //文件指定KEY
        public string _fileKey = "file";
        //TODO 密钥
    }


    /// 
    /// multipart/form-data的解析器
    /// 
    internal class HttpMultipartParser
    {
        /// 
        /// 参数集合
        /// 
        public IDictionary<string, string> Parameters = new Dictionary<string, string>();
        /// 
        /// 上传文件部分参数
        /// 
        public string FilePartName { get; }
        /// 
        /// 是否解析成功
        /// 
        public bool Success { get; private set; }
        /// 
        /// 请求类型
        /// 
        public string ContentType { get; private set; }
        /// 
        /// 上传的文件名
        /// 
        public string Filename { get; private set; }
        /// 
        /// 上传的文件内容
        /// 
        public byte[] FileContents { get; private set; }

        /// 
        /// 解析multipart/form-data格式的文件请求,默认编码为utf8
        /// 
        /// 
        /// 
        public HttpMultipartParser(Stream stream, string filePartName)
        {
            FilePartName = filePartName;
            Parse(stream, Encoding.UTF8);
        }

        /// 
        /// 解析multipart/form-data格式的字符串
        /// 
        /// 
        public HttpMultipartParser(string content)
        {
            var array = Encoding.UTF8.GetBytes(content);
            var stream = new MemoryStream(array);
            Parse(stream, Encoding.UTF8);
        }

        /// 
        /// 解析multipart/form-data格式的文件请求
        /// 
        /// 
        /// 编码
        /// 
        public HttpMultipartParser(Stream stream, Encoding encoding, string filePartName)
        {
            FilePartName = filePartName;
            Parse(stream, encoding);
        }

        private void Parse(Stream stream, Encoding encoding)
        {
            Success = false;

            var data = ToByteArray(stream);

            var content = encoding.GetString(data);

            var delimiterEndIndex = content.IndexOf("\r\n", StringComparison.Ordinal);

            if (delimiterEndIndex > -1)
            {
                var delimiter = content.Substring(0, content.IndexOf("\r\n", StringComparison.Ordinal)).Trim();

                var sections = content.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);

                foreach (var s in sections)
                {
                    if (s.Contains("Content-Disposition"))
                    {
                        var nameMatch = new Regex(@"(?<=name\=\"")(.*?)(?=\"")").Match(s);
                        var name = nameMatch.Value.Trim().ToLower();

                        if (name == FilePartName && !string.IsNullOrEmpty(FilePartName))
                        {
                            var re = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
                            var contentTypeMatch = re.Match(content);

                            re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
                            var filenameMatch = re.Match(content);

                            if (contentTypeMatch.Success && filenameMatch.Success)
                            {
                                ContentType = contentTypeMatch.Value.Trim();
                                Filename = filenameMatch.Value.Trim();

                                var startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;

                                var delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
                                var endIndex = IndexOf(data, delimiterBytes, startIndex);

                                var contentLength = endIndex - startIndex;

                                var fileData = new byte[contentLength];

                                Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);

                                FileContents = fileData;
                            }
                        }
                        else if (!string.IsNullOrWhiteSpace(name))
                        {
                            var startIndex = nameMatch.Index + nameMatch.Length + "\r\n\r\n".Length;
                            Parameters.Add(name, s.Substring(startIndex).TrimEnd('\r', '\n').Trim());
                        }
                    }
                }

                if (FileContents != null || Parameters.Count != 0)
                {
                    Success = true;
                }
            }
        }

        public static int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
        {
            var index = 0;
            var startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);

            if (startPos != -1)
            {
                while (startPos + index < searchWithin.Length)
                {
                    if (searchWithin[startPos + index] == serachFor[index])
                    {
                        index++;
                        if (index == serachFor.Length)
                        {
                            return startPos;
                        }
                    }
                    else
                    {
                        startPos = Array.IndexOf(searchWithin, serachFor[0], startPos + index);
                        if (startPos == -1)
                        {
                            return -1;
                        }

                        index = 0;
                    }
                }
            }

            return -1;
        }

        public static byte[] ToByteArray(Stream stream)
        {
            var buffer = new byte[32768];
            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    var read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                    {
                        return ms.ToArray();
                    }

                    ms.Write(buffer, 0, read);
                }
            }
        }
    }

Zip Tool

对文件解压和回传加压

   /// 
    /// 适用与ZIP压缩
    /// 
    public class ZipHelper
    {
        #region 压缩指定文件 但不需要保存到本地
        /// 
        /// 功能:压缩文件 
        /// 
        /// 指定文件
        /// 压缩文件的字节流
        /// 是否压缩成功
        public static bool ZipFile(string file, string zipName, out byte[] buffer)
        {
            try
            {
                byte[] byteArray = File.ReadAllBytes(file);

                MemoryStream ms = new MemoryStream();

                using (ZipOutputStream zips = new ZipOutputStream(ms))
                {
                    zips.SetLevel(9); // 0 - store only to 9 - means best compression

                    var entry = new ZipEntry(zipName);
                    entry.DateTime = DateTime.Now;
                    zips.PutNextEntry(entry);
                    zips.Write(byteArray, 0, byteArray.Length);
                    zips.Finish();
                }

                buffer = ms.ToArray(); //这里是一个zip压缩的文件流
                return true;

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region 解压
        /// 
        /// 功能:解压zip格式的文件。
        /// 
        /// 压缩文件路径
        /// 解压文件存放路径,为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹
        /// 出错信息
        /// 解压是否成功
        public static bool UnZipFile(string zipFilePath, string unZipDir)
        {
            try
            {
                //解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹
                if (unZipDir == string.Empty)
                    unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
                if (!unZipDir.EndsWith("//"))
                    unZipDir += "//";
                if (!Directory.Exists(unZipDir))
                    Directory.CreateDirectory(unZipDir);

                using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipFilePath)))
                {

                    ZipEntry theEntry;
                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        string directoryName = Path.GetDirectoryName(theEntry.Name);
                        string fileName = Path.GetFileName(theEntry.Name);
                        if (directoryName.Length > 0)
                        {
                            Directory.CreateDirectory(unZipDir + directoryName);
                        }
                        if (!directoryName.EndsWith("//"))
                            directoryName += "//";
                        if (fileName != String.Empty)
                        {
                            using (FileStream streamWriter = File.Create(unZipDir + theEntry.Name))
                            {

                                int size = 2048;
                                byte[] data = new byte[2048];
                                while (true)
                                {
                                    size = s.Read(data, 0, data.Length);
                                    if (size > 0)
                                    {
                                        streamWriter.Write(data, 0, size);
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }//while
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return false;
            }
            return true;
        }//解压结束
        #endregion

    }

Base64工具类封装

一些图片,文件的base64处理

    class Base64Helper
    {
        public static string Img2base64(string img_path)
        {

            if (string.IsNullOrEmpty(img_path))
                return "";

            //图片后缀格式
            int index = img_path.LastIndexOf('.');
            Image img = Image.FromFile(img_path);

            if (img != null && index != -1)
            {
                var suffix = img_path.Substring(index + 1, img_path.Length - 1 - index).ToLower();
                var format = ImageFormat.Png;
                switch (suffix) 
                {
                    case "jpg":
                    case "jpeg":
                        format = ImageFormat.Jpeg;
                        break;
                    case "bmp":
                        format = ImageFormat.Bmp;
                        break;
                    case "gif":
                        format = ImageFormat.Gif;
                        break;
                }

                using (MemoryStream ms = new MemoryStream())
                {
                    img.Save(ms, format);
                    byte[] data = ms.ToArray();
                    return $"data:image/{suffix};base64," + Convert.ToBase64String(data);
                }
            }


            return "";
        }

        public static Image Base642Image(string base64)
        {
            if (string.IsNullOrEmpty(base64))
            {
                throw new ArgumentNullException("base64参数不能为空");
            }
            byte[] bytes = Convert.FromBase64String(base64);
            Image img = null;
            using (MemoryStream memStream = new MemoryStream(bytes))
            {
                img = Image.FromStream(memStream);
            }
            return img;
        }
    
        public static string File2base64(string file)
        {
            return Convert.ToBase64String(File.ReadAllBytes(file));
        }
    }

基于PI的时钟加密key

由于 跨平台每个系统之间 PI的精度是存在误差的 这里我选取了 PI后10000位处的一段数组

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

namespace Geek7Utils
{
    class Sercert
    {
        static int[] pi10000 = new int[] {
            3,0,1,3,0,5,2,7,9,3,2,0,5,4,2,7,4,6,2,8,6,5,4,0,3,6,0,3,6,7,4,5,3,2,8,6,5,1,0,5,7,0,6,5,8,7,4,8,8,2,2,5,6,9,8,1,5,7,9,3,6,7,8,9,7,6,6,9,7,4,2,2,0,5,7,5,0,5,9,6,8,3,4,4,0,8,6,9,7,3,5,0,2,0,1,4,1,0,2,0,6,7,2,3,5,8,5,0,2,0,0,7,2,4,5,2,2,5,6,3,2,6,5,1,3,4,1,0,5,5,9,2,4,0,1,9,0,2,7,4,2,1,6,2,4,8,4,3,9,1,4,0,3,5,9,9,8,9,5,3,5,3,9,4,5,9,0,9,4,4,0,7,0,4,6,9,1,2,0,9
        };

        /// 
        /// 
        /// 
        ///  时间戳 
        /// 
        public static int GetSercert(int ts)
        {
            ts = ts < 0 ? 0 : ts;

            int[] cells = new int[8];
            int len = cells.Length;
            for (int i = 0; i < len; i++)
            {
                cells[i] = ts % (int)Math.Pow(10, i + 1);
            }
            int arrLen = pi10000.Length;
            int ID = 0;
            for (int i = 0; i < len; i++)
            {
                int index = cells[i] % arrLen;
                ID += pi10000[index] * (int)Math.Pow(10, i);
            }

            return ID;
        }


        /// 
        /// 获取指定时间的 时间戳 毫秒级
        /// 
        ///  当前时间 你可以传入 本地时间 或者 UTC 
        /// 时间戳 毫秒级
        public static Int64 GetTimeStamp(DateTime now)
        {
            TimeSpan ts = now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalMilliseconds);
        }

        /// 
        /// 将指定时间戳转换成DateTime对象
        /// 
        ///  时间戳 毫秒级 
        ///  DateTime对象 
        public static DateTime Convert2DateTime(Int64 milllTimeStamp )
        {
            return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddMilliseconds(milllTimeStamp);
        }

        /// 
        /// 验证包
        /// 
        /// 
        /// 
        /// 
        public static bool Verify(string ts, string secret)
        {
            Int64 timestamp = Convert.ToInt64(ts);
            var time = Convert2DateTime(timestamp);

            if (Math.Abs(DateTime.Now.Second - time.Second) < 5)
            {
                return GetSercert((int)timestamp) == Convert.ToInt32(secret);
            }

            return false;
        }
    }
}

仓库地址

Gitee https://gitee.com/welcome2jcSpace/simple-http
如何在前端html上传文件到服务器处理并反馈给前端展示_第1张图片

你可能感兴趣的:(Web3,C#,jquery,ajax,http,javascript,c#)