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);
});
}
在将待处理文件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);
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({
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);
}
},
});
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压缩
///
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处理
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的精度是存在误差的 这里我选取了 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;
}
}
}