[一键CV] Blazor 拖放上传文件转换格式并推送到浏览器下载

news2024/12/26 0:34:28

前言

昨天有个小伙伴发了一个老外java编写的小工具给我,功能是转换西班牙邮局快递Coreeos express的单据格式成Amazon格式,他的需求是改一下程序为匹配转换另一个快递公司MRW格式到Amazon格式,然而我堂堂一个Blazor发烧友,怎么可能去反编译人家的java修改呢?必须直接撸一个Blazor的啊.

实现目标

拖放多文件上传, 不落地(无中间保存临时文件)直接内存流转换格式, 直接推送弹出下载文件.

分析需求

原始MRW文件.txt

"Abonado","Depto.","Fecha","N. Envio","N. Lote","Tipo de Cobro","Bultos","Kg.","Imp Reemb.","Referencia","Destinatario","Direccion","C.P.","Poblacion","Pais","Servicio","Retorno Alb.",""
"xxx  SL ","N/A","15/02/2023","0263608650029","02636xxx20230214204409","Pagados","1","1","","403-6273741-3115504","Antonia xxx FERNANDEZ","C/MENDEZ NUÑEZ 222","06420","CASTUERA","ESPAÑA","U19E--Urgente 19 Expedición","SinRetorno",""
"xxx  SL ","N/A","15/02/2023","0263608650028","02636xxx20230214204409","Pagados","1","1","","406-8908494-9500324","Baris xxx","Parque Erreniega Parkea,","31180","CIZUR MAYOR","ESPAÑA","U19E--Urgente 19 Expedición","SinRetorno",""

实体类

来源

  public class MrwTicket
    {
        public string Abonado { get; set; }

        [DisplayName("Depto.")]
        public string Depto { get; set; }

        public DateTime Fecha { get; set; }

        [DisplayName("N. Envio")]
        public string N_Envio { get; set; }

        [DisplayName("N. Lote")]
        public string N_Lote { get; set; }

        [DisplayName("Tipo de Cobro")]
        public string TipoDeCobro { get; set; }

        public string Bultos { get; set; }

        [DisplayName("Kg.")]
        public string Kg { get; set; }

        [DisplayName("Imp Reemb.")]
        public string ImpReemb { get; set; }


        public string Referencia { get; set; }


        public string Destinatario { get; set; }


        public string Direccion { get; set; }

        [DisplayName("C.P.")]
        public string CP { get; set; }

        public string Poblacion { get; set; }

        public string Pais { get; set; }

        public string Servicio { get; set; }

        [DisplayName("Retorno Alb.")]
        public string RetornoAlb { get; set; }
    }

转换目标

    public class AmazonTicket
    {

        [DisplayName("order-id")]
        public string Order_id { get; set; }

        [DisplayName("order-item-id")]
        public string Order_item_id { get; set; }

        [DisplayName("quantity")]
        public string Quantity { get; set; }

        [DisplayName("ship-date")]
        public string Ship_date { get; set; }

        [DisplayName("carrier-code")]
        public string Carrier_code { get; set; }

        [DisplayName("carrier-name")]
        public string Carrier_name { get; set; }

        [DisplayName("tracking-number")]
        public string Tracking_number { get; set; }

        [DisplayName("ship-method")]
        public string Ship_method { get; set; }

    }

建立Blazor页面 Mrw2Amazon.razor

拖放上传可以参考往期文章 https://www.cnblogs.com/densen2014/p/16128246.html

组件UI

@page "/Mrw2Amazon"
@inherits PublicComponentsBase
@namespace AmeBlazor.Components

<h4>MRW txt 转 Amazon txt</h4>

<PageTitle>MRW txt 转 Amazon txt</PageTitle>

<div @ref="UploadElement" style="padding: 20px; width: 200px; height: 200px; background-color: cornflowerblue; border: 2px dashed #0087F7; border-radius: 5px; ">
    <p>拖放上传文件</p>
    <InputFile OnChange="OnChange" class="form-control" multiple @ref="inputFile" />
</div>

<pre>
<code>
        @uploadstatus
</code>
</pre>

拖放上传js文件 wwwroot/drag.js

export function init(wrapper, element, inputFile) {

    //阻止浏览器默认行为
    document.addEventListener("dragleave", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("drop", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("dragenter", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("dragover", function (e) {
        e.preventDefault();
    }, false); 

    element.addEventListener("drop", function (e) {

        try {
            var fileList = e.dataTransfer.files; //获取文件对象
            //检测是否是拖拽文件到页面的操作
            if (fileList.length == 0) {
                return false;
            }

            inputFile.files = e.dataTransfer.files;
            const event = new Event('change', { bubbles: true });
            inputFile.dispatchEvent(event);
        }
        catch (e) {
            wrapper.invokeMethodAsync('DropAlert', e);
        }
    }, false);

    element.addEventListener('paste', function (e) {
    
        inputFile.files = e.clipboardData.files;
        const event = new Event('change', { bubbles: true });
        inputFile.dispatchEvent(event);
    }, false);

    return {
        dispose: () => {
            element.removeEventListener('dragleave', onDragLeave);
            element.removeEventListener("drop", onDrop);
            element.removeEventListener('dragenter', onDragHover);
            element.removeEventListener('dragover', onDragHover);
            element.removeEventListener('paste', handler);
        }
    }
}

下载功能

Pages\_Layout.cshtml < /body >之前添加js代码

    <script>
        window.downloadFileFromStream = async (fileName, contentStreamReference) => {
            const arrayBuffer = await contentStreamReference.arrayBuffer();
            const blob = new Blob([arrayBuffer]);
            const url = URL.createObjectURL(blob);
            const anchorElement = document.createElement('a');
            anchorElement.href = url;
            anchorElement.download = fileName ?? '';
            anchorElement.click();
            anchorElement.remove();
            URL.revokeObjectURL(url);
        }
    </script>

组件代码Mrw2Amazon.razor.cs

先拉个库MiniExcel

<PackageReference Include="MiniExcel" Version="1.*" />

  1. 动态加载 drag.js 文件.(参考往期文章,js隔离 https://www.cnblogs.com/densen2014/p/16027851.html)
  2. 使用拖放读取到 IBrowserFile 文件流
  3. 转换为 MemoryStream 供给 MiniExcel 读取. (PS:不能直接使用 IBrowserFile 的 stream , 当作课后作业自己了解一下.)
  4. MiniExcel 读取格式: var mrwTicket = MiniExcel.Query(fs, excelType: ExcelType.CSV).ToList();
  5. 转换格式
  6. 另存为目标格式csv
  7. 直接弹出目标文件下载到浏览器
  8. 全程不落地
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using MiniExcelLibs;
using MiniExcelLibs.Csv;

    public partial class Mrw2Amazon : IAsyncDisposable
    {

        [Inject]
        IJSRuntime JS { get; set; }
        
        //既然不落地,那就不需要这些了
        //[Inject] 
        //protected Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnvironment { get; set; }

        protected ElementReference UploadElement { get; set; }
        protected InputFile? inputFile { get; set; }

        private DotNetObjectReference<Mrw2Amazon>? wrapper;

        private IJSObjectReference? module;
        private IJSObjectReference? dropInstance;

        //既然不落地,那就不需要这些了
        //protected string UploadPath = "";
        
        protected string? uploadstatus;
        long maxFileSize = 1024 * 1024 * 15;

        //既然不落地,那就不需要这些了
        //protected override void OnAfterRender(bool firstRender)
        //{
            //if (!firstRender) return;
            //UploadPath = Path.Combine(HostEnvironment!.WebRootPath, "uploads", "temp"); //初始化上传路径
            //if (!Directory.Exists(UploadPath)) Directory.CreateDirectory(UploadPath); //不存在则新建目录
        //}

        protected async Task OnChange(InputFileChangeEventArgs e)
        {
            int i = 0;
            var selectedFiles = e.GetMultipleFiles(100);
            foreach (var item in selectedFiles)
            {
                i++;
                await OnSubmit(item);
                uploadstatus += Environment.NewLine + $"[{i}]: " + item.Name;
            }
        }

        protected async Task OnSubmit(IBrowserFile efile)
        {
            try
            {

            if (efile == null) return;
            if (efile.ContentType != "text/plain")
            {
                uploadstatus += Environment.NewLine + $"只接受txt文件.{efile.Name}为{efile.ContentType}";
                return;
            }
            await using var fs = new MemoryStream();
            using var stream = efile.OpenReadStream(maxFileSize);

            await stream.CopyToAsync(fs);

            var mrwTicket = MiniExcel.Query<MrwTicket>(fs, excelType: ExcelType.CSV).ToList();
            var amazonTicket = new List<AmazonTicket>();
            foreach (var item2 in mrwTicket)
            {
                amazonTicket.Add(new AmazonTicket()
                {
                    Order_id = item2.Referencia,
                    Ship_date = item2.Fecha.ToString("MM-dd-yyyy"),
                    Carrier_code = "MRW",
                    Tracking_number = item2.N_Envio.Remove(5, 1),
                    Ship_method = "Urgente 19",
                });
            }

            var memoryStream = new MemoryStream();
            memoryStream.SaveAs(amazonTicket, excelType: ExcelType.CSV, configuration: new CsvConfiguration() { Seperator = '\t' });
            memoryStream.Seek(0, SeekOrigin.Begin);
            using var streamRef = new DotNetStreamReference(stream: memoryStream);

            await JS.InvokeVoidAsync("downloadFileFromStream", Path.GetFileNameWithoutExtension(efile.Name) + "_amazon.txt", streamRef);

            uploadstatus += Environment.NewLine + $"{efile.Name} 转换OK";

            }
            catch (Exception e)
            {
                uploadstatus += Environment.NewLine + $"转换出错 {e.Message}";
            }
            StateHasChanged();
        }


        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (!firstRender) return;

            module = await JS.InvokeAsync<IJSObjectReference>("import", "./drag.js");
            wrapper = DotNetObjectReference.Create(this);
            dropInstance = await module.InvokeAsync<IJSObjectReference>("init", wrapper, UploadElement, inputFile!.Element);
        }

        [JSInvokable]
        public void DropAlert(string msg)
        {
            uploadstatus += Environment.NewLine + $"[!Alert!]: " + msg;
            StateHasChanged();
        }


        async ValueTask IAsyncDisposable.DisposeAsync()
        {
            if (dropInstance != null)
            {
                await dropInstance.InvokeVoidAsync("dispose");
                await dropInstance.DisposeAsync();
            }

            if (wrapper != null)
            {
                wrapper.Dispose();
            }

            if (module != null)
            {
                await module.DisposeAsync();
            }
        }

    }

运行

可接受多文件拖放同时转换

完整代码来的,直接cv应该可以用了.


关联项目

FreeSql QQ群:4336577

BA & Blazor QQ群:795206915

Maui Blazor 中文社区 QQ群:645660665

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。

转载声明

本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接.

AlexChow

今日头条 | 博客园 | 知乎 | Gitee | GitHub

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/354213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 快速上手学习入门教程

目录 1、docker 的基础概念 2、怎样打包和运行一个应用程序&#xff1f; 3、如何对 docker 中的应用程序进行修改&#xff1f; 4、如何对创建的镜像进行共享&#xff1f; 5、如何使用 volumes 名称对容器中的数据进行存储&#xff1f;// 数据挂载 6、另一种挂载方式&…

Mongodb WT_PANIC: WiredTiger library panic

文章目录故障现象排查过程1.查看Log2.同步恢复数据故障现象 周五突然收到Mongo实例莫名奇妙挂了告警&#xff0c;一般都是RS复制集架构模式&#xff08;5节点&#xff09;&#xff0c;查看此实例角色为SECONDAR&#xff0c;挂了暂时不影响线上业务&#xff0c;但还是需要尽快修…

前端智能化在淘宝的2022实践总结

过去十年是智能化蓬勃发展的十年&#xff0c;但未来十年会是智能化渗入各领域彻底改变我们生活和工作的十年。阿里前端智能化方向小组历经 4 年的实践和演变&#xff0c;在前端融入业务技术团队和终端融合的背景之下&#xff0c;前端智能化小组在2022年更多以优化拓展基础业务工…

【计算机网络】因特网概述

文章目录因特网概述网络、互联网和因特网互联网历史与ISP标准化与RFC因特网的组成三种交换方式电路交换分组交换和报文交换三种交换方式的对比与总结计算机网络的定义和分类计算机网络的定义计算机网络的分类计算机网络的性能指标速率带宽吞吐量时延时延带宽积往返时间利用率丢…

球员分析-前锋

1、球员位置 1.1柱式中锋 球员&#xff1a;吉鲁、奥斯梅恩、米特罗维奇 1.2防守型前锋 球员&#xff1a;劳塔罗、瓦尔迪、维尔纳 1.3抢点前锋 球员&#xff1a;伊卡尔迪、曼联c罗、因扎吉 1.4组织型前锋 球员&#xff1a;凯恩、本泽马、迪巴拉 2、战术职责 2.1柱式中锋&#xf…

设计模式-状态机模式

参考 什么是状态机&#xff1f; 设计模式-状态机模式 什么是状态机(有限状态自动机) 可以把状态机比作方程式, 你输入当前信息, 就能得到下一个信息 举个例子, 按钮门有两个状态, 关闭状态和打开状态, 如果按下开门按钮, 门的状态就从关闭到打开 状态机就是接受门当前状态…

极兔一面:10亿级ES海量搜索狂飙10倍,该怎么办?

背景说明&#xff1a; ES高性能全文索引&#xff0c;如果不会用&#xff0c;或者没有用过&#xff0c;在面试中&#xff0c;会非常吃亏。 所以ES的实操和底层原理&#xff0c;大家要好好准备。 另外&#xff0c;ES调优是一个非常、非常核心的面试知识点&#xff0c;大家要非…

就业大山之下的网络安全:安逸的安服仔

从去年开始&#xff0c;各个互联网大厂就接二连三的放出了裁员消息&#xff0c;整个互联网行业好像都处于寒冬状态。微博、小米、滴滴、知乎、拼多多等在内的一大批互联网知名企业&#xff0c;也相继传出“人员优化”的消息。 除了国内市场的萧条&#xff0c;国外市场也是不容…

kubernetes教程 --Pod调度

Pod调度 在默认情况下&#xff0c;一个Pod在哪个Node节点上运行&#xff0c;是由Scheduler组件采用相应的算法计算出来的&#xff0c;这个过程是不受人工控制的。但是在实际使用中&#xff0c;这并不满足的需求&#xff0c;因为很多情况下&#xff0c;我们想控制某些Pod到达某…

springboot simple (13) springboot Elasticsearch(Elasticsearch8.5.1)

这里首先简单的介绍了Elasticsearch&#xff0c;然后实现了springboot集成Elasticsearch。 版本&#xff1a; Elasticsearch&#xff1a;v8.5.1 Kibana&#xff1a;v8.5.1 springboot集成elasticsearch有两种方式。 1&#xff09;rest客户端RestHingLevelClient&#xff1b; …

2.2 BeautifulSoup 装载HTML文档

HTML文档结点的查找工具很多&#xff0c;其中 BeautifulSoup 是功能强大且十分流行的查找工具之一。1. BeautifulSoup 的安装安装&#xff1a;pip install bs4导包&#xff1a;from bs4 import BeautifulSoup2. BeautifulSoup 装载HTML文档如果 doc 是一个 HTML 文档&#xff0…

fast planner总结

一、前端 kinodynamic A*算法动力学路径搜索 1.1 路径搜索的主要函数为kinodynamicAstar类的search函数 int KinodynamicAstar::search(Eigen::Vector3d start_pt, Eigen::Vector3d start_v, Eigen::Vector3d start_a,Eigen::Vector3d end_pt, Eigen::Vector3d end_v, bool ini…

DPDK — Userspace PMD 源码分析

目录 文章目录目录PMD driver 通过 IGB_UIO 与 UIO 进行交互注册一个 UIO 设备PMD 的应用层实现PMD 同样支持中断处理方式PMD driver 通过 IGB_UIO 与 UIO 进行交互 IGB_UIO 内核模块的另一个主要功能就是让用于态的 PMD 网卡驱动程序得以与 UIO 进行交互。对于 PMD 的实现来说…

松下PLC通过fpwin上传写入MRTC模块方法

目录 PLC程序上传方法 加密模块使用 PLC程序上传方法 手动将PLC模式设置为prog模式查看PLC是否设置为禁止上传查询指示灯是否变蓝&#xff0c;变蓝则需要将PLC禁止上传功能取消。 3.当上述动作操作完成后&#xff0c;将PLC程序导入到PLC中。为了配合加密程序使用&#xff0c;…

进程通信方式

无名管道( pipe )&#xff1a; 管道是一种半双工的通信方式&#xff0c;数据只能单向流动&#xff0c;而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。高级管道&#xff08;popen&#xff09;&#xff1a; 将另一个程序当做一个新的进程在当前程序进…

主成分分析(PCA)原理详解

1. 相关背景 在许多领域的研究与应用中&#xff0c;通常需要对含有多个变量的数据进行观测&#xff0c;收集大量数据后进行分析寻找规律。多变量大数据集无疑会为研究和应用提供丰富的信息&#xff0c;但是也在一定程度上增加了数据采集的工作量。更重要的是在很多情形下&…

Windows server——部署web服务

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 本章重点 一.web讲解 1.WWW概述 &#xff08;1&#xff09;WWW服务概述 &…

Linux应用编程下连接本地数据库进行增删改查系列操作

文章目录前言一、常用SQL操作语句二、相关函数解析三、连接本地数据库四、编译运行五、程序源码前言 本篇为C语言应用编程下连接Linux本地数据库进行增删改查系列操作。 在此之前&#xff0c;首先当然是你需要具备一定的数据库基础&#xff0c;所以下面我先列出部分常用的SQL…

如何利用文件上传漏洞-以及如何修复它们

目录什么是文件上传漏洞&#xff1f;文件上传请求如何工作为什么文件上传漏洞是个问题&#xff1f;漏洞 #1&#xff1a;通过文件内容远程代码执行&#xff08;Web Shell 上传&#xff09;绕过黑名单保护案例 1&#xff1a;案例 2&#xff1a;案例 3&#xff1a;案例 4&#xff…

【Spark分布式内存计算框架——Spark SQL】10. External DataSource(上)数据源与格式

第六章 External DataSource 在SparkSQL模块&#xff0c;提供一套完成API接口&#xff0c;用于方便读写外部数据源的的数据&#xff08;从Spark 1.4版本提供&#xff09;&#xff0c;框架本身内置外部数据源&#xff1a; 在Spark 2.4版本中添加支持Image Source&#xff08;图…