GVKun编程网logo

asp.net – 使用vb.net和EPPlus的单元格包装(vb.net imports)

7

在本文中,您将会了解到关于asp.net–使用vb.net和EPPlus的单元格包装的新资讯,同时我们还将为您解释vb.netimports的相关在本文中,我们将带你探索asp.net–使用vb.ne

在本文中,您将会了解到关于asp.net – 使用vb.net和EPPlus的单元格包装的新资讯,同时我们还将为您解释vb.net imports的相关在本文中,我们将带你探索asp.net – 使用vb.net和EPPlus的单元格包装的奥秘,分析vb.net imports的特点,并给出一些关于(六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet、ASP.NET Core中使用EPPlus导入出Excel文件的完整步骤、ASP.NET Core使用EPPlus操作Excel、ASP.NET MVC使用EPPlus,导出数据到Excel中的实用技巧。

本文目录一览:

asp.net – 使用vb.net和EPPlus的单元格包装(vb.net imports)

asp.net – 使用vb.net和EPPlus的单元格包装(vb.net imports)

我正在使用EPPlus和VB.net / ASP.Net在一个单元格中包装一个值
有没有人能够做到这一点?我搜索了SO&谷歌,但没有运气.

解决方法

这将包装一个单元格的值(EPPlus 3.0.0.2)c#代码:
workSheet.Cells[1,1].Style.WrapText = true;

(六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet

(六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet

 Pod控制器相关知识

控制器的必要性

自主式Pod对象由调度器调度到目标工作节点后即由相应节点上的kubelet负责监控其容器的存活状态,容器主进程崩溃后,kubelet能够自动重启相应的容器。但对出现非主进程崩溃类的容器错误却无从感知,这便依赖于pod资源对象定义的存活探测,以便kubelet能够探知到此类故障。但若pod被删除或者工作节点自身发生故障(工作节点上都有kubeletkubelet不可用,因此其健康状态便无法保证),则便需要控制器来处理相应的容器重启和配置。

常见的工作负载控制器

Pod控制器由masterkube-controller-manager组件提供,常见的此类控制器有:

ReplicationController

ReplicaSet:代用户创建指定数量的pod副本数量,确保pod副本数量符合预期状态,并且支持滚动式自动扩容和缩容功能

Deployment:工作在ReplicaSet之上,用于管理无状态应用,目前来说最好的控制器。支持滚动更新和回滚功能,还提供声明式配置。

DaemonSet:用于确保集群中的每一个节点只运行特定的pod副本,常用于实现系统级**后台任务。比如ELK服务

StatefulSet:管理有状态应用

Job:只要完成就立即退出,不需要重启或重建

CronJob:周期性任务控制,不需要持续后台运行

Pod控制器概述

Kubernetes的核心功能之一还在于要确保各资源对象的当前状态(status)以匹配用户期望的状态(spec),使当前状态不断地向期望状态“和解”(reconciliation)来完成容器应用管理。而这些则是kube-controller-manager的任务。

创建为具体的控制器对象之后,每个控制器均通过API Server提供的接口持续监控相关资源对象的当前状态,并在因故障、更新或其他原因导致系统状态发生变化时,尝试让资源的当前状态想期望状态迁移和逼近。

List-Watchkubernetes实现的核心机制之一,在资源对象的状态发生变动时,由API Server负责写入etcd并通过水平触发(level-triggered)机制主动通知给相关的客户端程序以确保其不会错过任何一个事件。控制器通过API Serverwatch接口实时监控目标资源对象的变动并执行和解操作,但并不会与其他控制器进行任何交互。

Pod和Pod控制器

Pod控制器资源通过持续性地监控集群中运行着的Pod资源对象来确保受其管控的资源严格符合用户期望的状态,例如资源副本的数量要精确符合期望等。通常,一个Pod控制器资源至少应该包含三个基本的组成部分:

标签选择器:匹配并关联Pod资源对象,并据此完成受其管控的Pod资源计数。

期望的副本数:期望在集群中精确运行着的Pod资源的对象数量。

Pod模板:用于新建Pod资源对象的Pod模板资源。

ReplicaSet控制器

ReplicaSet概述

ReplicaSet是取代早期版本中的ReplicationController控制器,其功能基本上与ReplicationController相同

ReplicaSet(简称RS)是Pod控制器类型的一种实现,用于确保由其管控的Pod对象副本数在任意时刻都能精确满足期望的数量。ReplicaSet控制器资源启动后会查找集群中匹配器标签选择器的Pod资源对象,当前活动对象的数量与期望的数量不吻合时,多则删除,少则通过Pod模板创建以补足。

ReplicaSet能够实现以下功能:

  • 确保Pod资源对象的数量精确反映期望值:ReplicaSet需要确保由其控制运行的Pod副本数量精确吻合配置中定义的期望值,否则就会自动补足所缺或终止所余。

  • 确保Pod健康运行:探测到由其管控的Pod对象因其所在的工作节点故障而不可用时,自动请求由调度器于其他工作节点创建缺失的Pod副本。

  • 弹性伸缩:可通过ReplicaSet控制器动态扩容或者缩容Pod资源对象的数量。必要时还可以通过HPA控制器实现Pod资源规模的自动伸缩。

创建ReplicaSet

核心字段

spec字段一般嵌套使用以下几个属性字段:

replicas    <integer>:指定期望的Pod对象副本数量
selector    <Object>:当前控制器匹配Pod对象副本的标签选择器,支持matchLabels和matchExpressions两种匹配机制
template    <Object>:用于定义Pod时的Pod资源信息
minReadySeconds    <integer>:用于定义Pod启动后多长时间为可用状态,默认为0秒

ReplicaSet示例

#(1)命令行查看ReplicaSet清单定义规则
[root@k8s-master ~]# kubectl explain rs
[root@k8s-master ~]# kubectl explain rs.spec
[root@k8s-master ~]# kubectl explain rs.spec.template


#(2)创建ReplicaSet示例
[root@k8s-master ~]# vim manfests/rs-demo.yaml
apiVersion: apps/v1  #api版本定义
kind: ReplicaSet  #定义资源类型为ReplicaSet
metadata:  #元数据定义
  name: myapp
  namespace: default
spec:  #ReplicaSet的规格定义
  replicas: 2  #定义副本数量为2个
  selector:  #标签选择器,定义匹配Pod的标签
    matchLabels:
      app: myapp
      release: canary
  template:  #Pod的模板定义
    metadata:  #Pod的元数据定义
      name: myapp-pod  #自定义Pod的名称
      labels:  #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
        app: myapp
        release: canary
    spec:  #Pod的规格定义
      containers:  #容器定义
      - name: myapp-containers  #容器名称
        image: ikubernetes/myapp:v1  #容器镜像
        imagePullPolicy: IfNotPresent  #拉取镜像的规则
        ports:  #暴露端口
        - name: http  #端口名称
          containerPort: 80
 

#(3)创建ReplicaSet定义的Pod
[root@k8s-master ~]# kubectl apply -f manfests/rs-demo.yaml
replicaset.apps/myapp created
[root@k8s-master ~]# kubectl get rs    #查看创建的ReplicaSet控制器
NAME    DESIRED   CURRENT   READY   AGE
myapp   4         4         4       3m23s
[root@k8s-master ~]# kubectl get pods   #通过查看pod可以看出pod命令是规则是前面是replicaset控制器的名称加随机生成的字符串
NAME          READY   STATUS    RESTARTS   AGE
myapp-bln4v   1/1     Running   0          6s
myapp-bxpzt   1/1     Running   0          6s


#(4)修改Pod的副本数量
[root@k8s-master ~]# kubectl edit rs myapp
  replicas: 4
[root@k8s-master ~]# kubectl get rs -o wide 
NAME    DESIRED   CURRENT   READY   AGE    CONTAINERS         IMAGES                 SELECTOR
myapp   4         4         4       2m50s   myapp-containers   ikubernetes/myapp:v2   app=myapp,release=canary
[root@k8s-master ~]# kubectl get pods --show-labels
NAME          READY   STATUS    RESTARTS   AGE     LABELS
myapp-8hkcr   1/1     Running   0          2m2s    app=myapp,release=canary
myapp-bln4v   1/1     Running   0          3m40s   app=myapp,release=canary
myapp-bxpzt   1/1     Running   0          3m40s   app=myapp,release=canary
myapp-ql2wk   1/1     Running   0          2m2s    app=myapp,release=canary

更新ReplicaSet控制器

修改Pod模板:升级应用

修改上面创建的replicaset示例文件,将镜像ikubernetes/myapp:v1改为v2版本

[root@k8s-master ~]# vim manfests/rs-demo.yaml
    spec:  #Pod的规格定义
      containers:  #容器定义
      - name: myapp-containers  #容器名称
        image: ikubernetes/myapp:v2  #容器镜像
        imagePullPolicy: IfNotPresent  #拉取镜像的规则
        ports:  #暴露端口
        - name: http  #端口名称
          containerPort: 80
[root@k8s-master ~]# kubectl apply -f manfests/rs-demo.yaml  #执行apply让其重载
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
Name          Image
myapp-bln4v   ikubernetes/myapp:v1
myapp-bxpzt   ikubernetes/myapp:v1

#说明:这里虽然重载了,但是已有的pod所使用的镜像仍然是v1版本的,只是新建pod时才会使用v2版本,这里测试先手动删除已有的pod。
[root@k8s-master ~]# kubectl delete pods -l app=myapp  #删除标签app=myapp的pod资源
pod "myapp-bln4v" deleted
pod "myapp-bxpzt" deleted
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image   #再次查看通过ReplicaSet新建的pod资源对象。镜像已使用v2版本
Name          Image
myapp-mdn8j   ikubernetes/myapp:v2
myapp-v5bgr   ikubernetes/myapp:v2

扩容和缩容

可以直接通过vim 编辑清单文件修改replicas字段,也可以通过kubect edit 命令去编辑。kubectl还提供了一个专用的子命令scale用于实现应用规模的伸缩,支持从资源清单文件中获取新的目标副本数量,也可以直接在命令行通过“--replicas”选项进行读取。

[root@k8s-master ~]# kubectl get rs    #查看ReplicaSet
NAME    DESIRED   CURRENT   READY   AGE
myapp   2         2         2       154m
[root@k8s-master ~]# kubectl get pods    #查看Pod
NAME          READY   STATUS    RESTARTS   AGE
myapp-mdn8j   1/1     Running   0          5m26s
myapp-v5bgr   1/1     Running   0          5m26s

#扩容
[root@k8s-master ~]# kubectl scale replicasets myapp --replicas=5    #将上面的Deployments控制器myapp的Pod副本数量提升为5个
replicaset.extensions/myapp scaled
[root@k8s-master ~]# kubectl get rs    #查看ReplicaSet
NAME    DESIRED   CURRENT   READY   AGE
myapp   5         5         5       156m
[root@k8s-master ~]# kubectl get pods    #查看Pod
NAME          READY   STATUS    RESTARTS   AGE
myapp-lrrp8   1/1     Running   0          8s
myapp-mbqf8   1/1     Running   0          8s
myapp-mdn8j   1/1     Running   0          6m48s
myapp-ttmf5   1/1     Running   0          8s
myapp-v5bgr   1/1     Running   0          6m48s

#收缩
[root@k8s-master ~]# kubectl scale replicasets myapp --replicas=3
replicaset.extensions/myapp scaled
[root@k8s-master ~]# kubectl get rs
NAME    DESIRED   CURRENT   READY   AGE
myapp   3         3         3       159m
[root@k8s-master ~]# kubectl get pods 
NAME          READY   STATUS    RESTARTS   AGE
myapp-mdn8j   1/1     Running   0          10m
myapp-ttmf5   1/1     Running   0          3m48s
myapp-v5bgr   1/1     Running   0          10m

删除ReplicaSet控制器资源

使用Kubectl delete命令删除ReplicaSet对象时默认会一并删除其管控的各Pod对象,有时,考虑到这些Pod资源未必由其创建,或者即便由其创建也并非自身的组成部分,这时候可以添加“--cascade=false”选项,取消级联关系。

[root@k8s-master ~]# kubectl get rs
NAME    DESIRED   CURRENT   READY   AGE
myapp   3         3         3       162m
[root@k8s-master ~]# kubectl get pods 
NAME          READY   STATUS    RESTARTS   AGE
myapp-mdn8j   1/1     Running   0          12m
myapp-ttmf5   1/1     Running   0          6m18s
myapp-v5bgr   1/1     Running   0          12m
[root@k8s-master ~]# kubectl delete replicasets myapp --cascade=false
replicaset.extensions "myapp" deleted
[root@k8s-master ~]# kubectl get rs
No resources found.
[root@k8s-master ~]# kubectl get pods 
NAME          READY   STATUS    RESTARTS   AGE
myapp-mdn8j   1/1     Running   0          13m
myapp-ttmf5   1/1     Running   0          7m
myapp-v5bgr   1/1     Running   0          13m

#通过上面的示例可以看出,添加--cascade=false参数后再删除ReplicaSet资源对象时并没有将其管控的Pod资源对象一并删除。

Deployment控制器

Deployment概述

Deployment(简写为deploy)是kubernetes控制器的又一种实现,它构建于ReplicaSet控制器之上,可为PodReplicaSet资源提供声明式更新。

Deployment控制器资源的主要职责是为了保证Pod资源的健康运行,其大部分功能均可通过调用ReplicaSet实现,同时还增添部分特性。

  • 事件和状态查看:必要时可以查看Deployment对象升级的详细进度和状态。

  • 回滚:升级操作完成后发现问题时,支持使用回滚机制将应用返回到前一个或由用户指定的历史记录中的版本上。

  • 版本记录:对Deployment对象的每一个操作都予以保存,以供后续可能执行的回滚操作使用。

  • 暂停和启动:对于每一次升级,都能够随时暂停和启动。

  • 多种自动更新方案:一是Recreate,即重建更新机制,全面停止、删除旧有的Pod后用新版本替代;另一个是RollingUpdate,即滚动升级机制,逐步替换旧有的Pod至新的版本。

创建Deployment

Deployment其核心资源和ReplicaSet相似

#(1)命令行查看ReplicaSet清单定义规则
[root@k8s-master ~]# kubectl explain deployment
[root@k8s-master ~]# kubectl explain deployment.spec
[root@k8s-master ~]# kubectl explain deployment.spec.template


#(2)创建Deployment示例
[root@k8s-master ~]# vim manfests/deploy-demo.yaml
apiVersion: apps/v1  #api版本定义
kind: Deployment  #定义资源类型为Deploymant
metadata:  #元数据定义
  name: deploy-demo  #deployment控制器名称
  namespace: default  #名称空间
spec:  #deployment控制器的规格定义
  replicas: 2  #定义副本数量为2个
  selector:  #标签选择器,定义匹配Pod的标签
    matchLabels:
      app: deploy-app
      release: canary
  template:  #Pod的模板定义
    metadata:  #Pod的元数据定义
      labels:  #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
        app: deploy-app
        release: canary
    spec:  #Pod的规格定义
      containers:  #容器定义
      - name: myapp  #容器名称
        image: ikubernetes/myapp:v1  #容器镜像
        ports:  #暴露端口
        - name: http  #端口名称
          containerPort: 80



#(3)创建Deployment对象
[root@k8s-master ~]# kubectl apply -f manfests/deploy-demo.yaml 
deployment.apps/deploy-demo created



#(4)查看资源对象
[root@k8s-master ~]# kubectl get deployment    #查看Deployment资源对象
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
deploy-demo   2/2     2            2           10s
[root@k8s-master ~]# kubectl get replicaset    #查看ReplicaSet资源对象
NAME                     DESIRED   CURRENT   READY   AGE
deploy-demo-78c84d4449   2         2         2       20s
[root@k8s-master ~]# kubectl get pods    #查看Pod资源对象
NAME                           READY   STATUS    RESTARTS   AGE
deploy-demo-78c84d4449-22btc   1/1     Running   0          23s
deploy-demo-78c84d4449-5fn2k   1/1     Running   0          23s
---
说明:
通过查看资源对象可以看出,Deployment会自动创建相关的ReplicaSet控制器资源,并以"[DEPLOYMENT-name]-[POD-TEMPLATE-HASH-VALUE]"格式为其命名,其中的hash值由Deployment自动生成。而Pod名则是以ReplicaSet控制器的名称为前缀,后跟5位随机字符。

更新策略

ReplicaSet控制器的应用更新需要手动分成多步并以特定的次序进行,过程繁杂且容易出错,而Deployment却只需要由用户指定在Pod模板中要改动的内容,(如镜像文件的版本),余下的步骤便会由其自动完成。Pod副本数量也是一样。

Deployment控制器支持两种更新策略:滚动更新(rollingUpdate)和重建创新(Recreate),默认为滚动更新

  • 滚动更新(rollingUpdate):即在删除一部分旧版本Pod资源的同时,补充创建一部分新版本的Pod对象进行应用升级,其优势是升级期间,容器中应用提供的服务不会中断,但更新期间,不同客户端得到的相应内容可能会来自不同版本的应用。

  • 重新创建(Recreate):即首先删除现有的Pod对象,而后由控制器基于新模板重行创建出新版本的资源对象。

Deployment控制器的滚动更新操作并非在同一个ReplicaSet控制器对象下删除并创建Pod资源,新控制器的Pod对象数量不断增加,直到旧控制器不再拥有Pod对象,而新控制器的副本数量变得完全符合期望值为止。如图所示

滚动更新时,应用还要确保可用的Pod对象数量不低于某阀值以确保可以持续处理客户端的服务请求,变动的方式和Pod对象的数量范围将通过kubectl explain deployment.spec.strategy.rollingUpdate.maxSurgekubectl explain deployment.spec.strategy.rollingUpdate.maxUnavailable两个属性同时进行定义。其功能如下:

  • maxSurge:指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;例如,如果期望值为3,当前的属性值为1,则表示Pod对象的总数不能超过4个。

  • maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望值的个数,其值可以是0或正整数,也可以是期望值的百分比;默认值为1,该值意味着如果期望值是3,则升级期间至少要有两个Pod对象处于正常提供服务的状态。

maxSurgemaxUnavailable属性的值不可同时为0,否则Pod对象的副本数量在符合用户期望的数量后无法做出合理变动以进行滚动更新操作。

Deployment控制器可以保留其更新历史中的旧ReplicaSet对象版本,所保存的历史版本数量由kubectl explain deployment.spec.revisionHistoryLimit参数指定。只有保存于revision历史中的ReplicaSet版本可用于回滚。

注:为了保存版本升级的历史,需要在创建Deployment对象时于命令中使用“--record”选项。

Deployment更新升级

修改Pod模板相关的配置参数便能完成Deployment控制器资源的更新。由于是声明式配置,因此对Deployment控制器资源的修改尤其适合使用applypatch命令来进行;如果仅只是修改容器镜像,“set image”命令更为易用。

1)首先通过set image命令将上面创建的Deployment对象的镜像版本改为v2版本

#打开1个终端进行升级
[root@k8s-master ~]# kubectl set image deployment/deploy-demo myapp=ikubernetes/myapp:v2 
deployment.extensions/deploy-demo image updated

#同时打开终端2进行查看pod资源对象升级过程
[root@k8s-master ~]# kubectl get pods -l app=deploy-app -w
NAME                           READY   STATUS    RESTARTS   AGE
deploy-demo-78c84d4449-2rvxr   1/1     Running   0          33s
deploy-demo-78c84d4449-nd7rr   1/1     Running   0          33s
deploy-demo-7c66dbf45b-7k4xz   0/1     Pending   0          0s
deploy-demo-7c66dbf45b-7k4xz   0/1     Pending   0          0s
deploy-demo-7c66dbf45b-7k4xz   0/1     ContainerCreating   0          0s
deploy-demo-7c66dbf45b-7k4xz   1/1     Running             0          2s
deploy-demo-78c84d4449-2rvxr   1/1     Terminating         0          49s
deploy-demo-7c66dbf45b-r88qr   0/1     Pending             0          0s
deploy-demo-7c66dbf45b-r88qr   0/1     Pending             0          0s
deploy-demo-7c66dbf45b-r88qr   0/1     ContainerCreating   0          0s
deploy-demo-7c66dbf45b-r88qr   1/1     Running             0          1s
deploy-demo-78c84d4449-2rvxr   0/1     Terminating         0          50s
deploy-demo-78c84d4449-nd7rr   1/1     Terminating         0          51s
deploy-demo-78c84d4449-nd7rr   0/1     Terminating         0          51s
deploy-demo-78c84d4449-nd7rr   0/1     Terminating         0          57s
deploy-demo-78c84d4449-nd7rr   0/1     Terminating         0          57s
deploy-demo-78c84d4449-2rvxr   0/1     Terminating         0          60s
deploy-demo-78c84d4449-2rvxr   0/1     Terminating         0          60s


#同时打开终端3进行查看pod资源对象变更过程
[root@k8s-master ~]# kubectl get deployment deploy-demo -w 
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
deploy-demo   2/2     2            2           37s
deploy-demo   2/2     2            2           47s
deploy-demo   2/2     2            2           47s
deploy-demo   2/2     0            2           47s
deploy-demo   2/2     1            2           47s
deploy-demo   3/2     1            3           49s
deploy-demo   2/2     1            2           49s
deploy-demo   2/2     2            2           49s
deploy-demo   3/2     2            3           50s
deploy-demo   2/2     2            2           51s


# 升级完成再次查看rs的情况,以下可以看到原的rs作为备份,而现在启动的是新的rs
[root@k8s-master ~]# kubectl get rs 
NAME                     DESIRED   CURRENT   READY   AGE
deploy-demo-78c84d4449   0         0         0       4m41s
deploy-demo-7c66dbf45b   2         2         2       3m54s

2)Deployment扩容

#1、使用kubectl scale命令扩容
[root@k8s-master ~]# kubectl scale deployment deploy-demo --replicas=3
deployment.extensions/deploy-demo scaled
[root@k8s-master ~]# kubectl get pods 
NAME                           READY   STATUS    RESTARTS   AGE
deploy-demo-7c66dbf45b-7k4xz   1/1     Running   0          10m
deploy-demo-7c66dbf45b-gq2tw   1/1     Running   0          3s
deploy-demo-7c66dbf45b-r88qr   1/1     Running   0          10m

#2、使用直接修改配置清单方式进行扩容
[root@k8s-master ~]# vim manfests/deploy-demo.yaml 
spec:  #deployment控制器的规格定义
  replicas: 4  #定义副本数量为2个
[root@k8s-master ~]# kubectl apply -f manfests/deploy-demo.yaml 
deployment.apps/deploy-demo configured
[root@k8s-master ~]# kubectl get pods 
NAME                           READY   STATUS    RESTARTS   AGE
deploy-demo-78c84d4449-6rmnm   1/1     Running   0          61s
deploy-demo-78c84d4449-9xfp9   1/1     Running   0          58s
deploy-demo-78c84d4449-c2m6h   1/1     Running   0          61s
deploy-demo-78c84d4449-sfxps   1/1     Running   0          57s

#3、使用kubectl patch打补丁的方式进行扩容
[root@k8s-master ~]# kubectl patch deployment deploy-demo -p ''{"spec":{"replicas":5}}''
deployment.extensions/deploy-demo patched
[root@k8s-master ~]# 
[root@k8s-master ~]# kubectl get pods 
NAME                           READY   STATUS    RESTARTS   AGE
deploy-demo-78c84d4449-6rmnm   1/1     Running   0          3m44s
deploy-demo-78c84d4449-9xfp9   1/1     Running   0          3m41s
deploy-demo-78c84d4449-c2m6h   1/1     Running   0          3m44s
deploy-demo-78c84d4449-sfxps   1/1     Running   0          3m40s
deploy-demo-78c84d4449-t7jxb   1/1     Running   0          3s

金丝雀发布

采用先添加再删除的方式,且可用Pod资源对象总数不低于期望值的方式进行,配置如下:

1)添加其总数多余期望值一个

[root@k8s-master ~]# kubectl patch deployment deploy-demo -p ''{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}''
deployment.extensions/deploy-demo patched

2)启动更新过程,在修改相应容器的镜像版本后立即暂停更新进度。

[root@k8s-master ~]# kubectl set image deployment/deploy-demo myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment deploy-demo 
deployment.extensions/deploy-demo image updated
deployment.extensions/deploy-demo paused


#查看
[root@k8s-master ~]# kubectl get deployment    #查看deployment资源对象
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
deploy-demo   6/5     1            6           37m
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image    #查看pod资源对象的name和image
Name                           Image
deploy-demo-6bf8dbdc9f-fjnzn   ikubernetes/myapp:v3
deploy-demo-78c84d4449-6rmnm   ikubernetes/myapp:v1
deploy-demo-78c84d4449-9xfp9   ikubernetes/myapp:v1
deploy-demo-78c84d4449-c2m6h   ikubernetes/myapp:v1
deploy-demo-78c84d4449-sfxps   ikubernetes/myapp:v1
deploy-demo-78c84d4449-t7jxb   ikubernetes/myapp:v1
[root@k8s-master ~]# kubectl rollout status deployment/deploy-demo    #查看更新情况
Waiting for deployment "deploy-demo" rollout to finish: 1 out of 5 new replicas have been updated...
---
#通过上面查看可以看出,当前的pod数量为6个,因为此前我们定义的期望值为5个,这里多出了一个,且这个镜像版本为v3版本。



#全部更新
[root@k8s-master ~]# kubectl rollout resume deployment deploy-demo
deployment.extensions/deploy-demo resumed
#再次查看
[root@k8s-master ~]# kubectl get deployment     #查看deployment资源对象
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
deploy-demo   5/5     5            5           43m
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image    #查看pod资源对象的name和image
Name                           Image
deploy-demo-6bf8dbdc9f-2z6gt   ikubernetes/myapp:v3
deploy-demo-6bf8dbdc9f-f79q2   ikubernetes/myapp:v3
deploy-demo-6bf8dbdc9f-fjnzn   ikubernetes/myapp:v3
deploy-demo-6bf8dbdc9f-pjf4z   ikubernetes/myapp:v3
deploy-demo-6bf8dbdc9f-x7fnk   ikubernetes/myapp:v3
[root@k8s-master ~]# kubectl rollout status deployment/deploy-demo     #查看更新情况
Waiting for deployment "deploy-demo" rollout to finish: 1 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 1 out of 5 new replicas have been updated...
Waiting for deployment spec update to be observed...
Waiting for deployment spec update to be observed...
Waiting for deployment "deploy-demo" rollout to finish: 1 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 1 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 2 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 2 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 2 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "deploy-demo" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "deploy-demo" rollout to finish: 1 old replicas are pending termination...
deployment "deploy-demo" successfully rolled out

回滚Deployment控制器下的应用发布

若因各种原因导致滚动更新无法正常进行,如镜像文件获取失败,等等;则应该将应用回滚到之前的版本,或者回滚到指定的历史记录中的版本。则通过kubectl rollout undo命令完成。如果回滚到指定版本则需要添加--to-revision选项

1)回到上一个版本

[root@k8s-master ~]# kubectl rollout undo deployment/deploy-demo
deployment.extensions/deploy-demo rolled back
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
Name                           Image
deploy-demo-78c84d4449-2xspz   ikubernetes/myapp:v1
deploy-demo-78c84d4449-f8p46   ikubernetes/myapp:v1
deploy-demo-78c84d4449-mnmvc   ikubernetes/myapp:v1
deploy-demo-78c84d4449-tsl7r   ikubernetes/myapp:v1
deploy-demo-78c84d4449-xdt8j   ikubernetes/myapp:v1

2)回滚到指定版本

#通过该命令查看更新历史记录
[root@k8s-master ~]# kubectl rollout history deployment/deploy-demo
deployment.extensions/deploy-demo 
REVISION  CHANGE-CAUSE
2         <none>
4         <none>
5         <none>

#回滚到版本2
[root@k8s-master ~]# kubectl rollout undo deployment/deploy-demo --to-revision=2
deployment.extensions/deploy-demo rolled back
[root@k8s-master ~]# kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
Name                           Image
deploy-demo-7c66dbf45b-42nj4   ikubernetes/myapp:v2
deploy-demo-7c66dbf45b-8zhf5   ikubernetes/myapp:v2
deploy-demo-7c66dbf45b-bxw7x   ikubernetes/myapp:v2
deploy-demo-7c66dbf45b-gmq8x   ikubernetes/myapp:v2
deploy-demo-7c66dbf45b-mrfdb   ikubernetes/myapp:v2

DaemonSet控制器

DaemonSet概述

DaemonSet用于在集群中的全部节点上同时运行一份指定Pod资源副本,后续新加入集群的工作节点也会自动创建一个相关的Pod对象,当从集群移除借点时,此类Pod对象也将被自动回收而无需重建。管理员也可以使用节点选择器及节点标签指定仅在具有特定特征的节点上运行指定的Pod对象。

应用场景

  • 运行集群存储的守护进程,如在各个节点上运行glusterdceph

  • 在各个节点上运行日志收集守护进程,如fluentdlogstash

  • 在各个节点上运行监控系统的代理守护进程,如Prometheus Node ExportercollectdDatadog agentNew Relic agentGanglia gmond等。

创建DaemonSet

DaemonSet控制器的spec字段中嵌套使用的相同字段selectortemplateminReadySeconds,并且功能和用法基本相同,但它不支持replicas,因为毕竟不能通过期望值来确定Pod资源的数量,而是基于节点数量。

这里使用nginx来示例,生产环境就比如使用上面提到的logstash等。

#(1) 定义清单文件
[root@k8s-master ~]# vim manfests/daemonset-demo.yaml
apiVersion: apps/v1    #api版本定义
kind: DaemonSet    #定义资源类型为DaemonSet
metadata:    #元数据定义
  name: daemset-nginx    #daemonset控制器名称
  namespace: default    #名称空间
  labels:    #设置daemonset的标签
    app: daem-nginx    
spec:    #DaemonSet控制器的规格定义
  selector:    #指定匹配pod的标签
    matchLabels:    #指定匹配pod的标签
      app: daem-nginx    #注意:这里需要和template中定义的标签一样
  template:    #Pod的模板定义
    metadata:    #Pod的元数据定义
      name: nginx  
      labels:    #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
        app: daem-nginx
    spec:    #Pod的规格定义
      containers:    #容器定义
      - name: nginx-pod    #容器名字
        image: nginx:1.12    #容器镜像
        ports:    #暴露端口
        - name: http    #端口名称
          containerPort: 80    #暴露的端口



#(2)创建上面定义的daemonset控制器
[root@k8s-master ~]# kubectl apply -f manfests/daemonset-demo.yaml 
daemonset.apps/daemset-nginx created



#(3)查看验证
[root@k8s-master ~]# kubectl get pods -o wide 
NAME                  READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
daemset-nginx-7s474   1/1     Running   0          80s   10.244.1.61   k8s-node1   <none>           <none>
daemset-nginx-kxpl2   1/1     Running   0          94s   10.244.2.58   k8s-node2   <none>           <none>
[root@k8s-master ~]# kubectl describe daemonset/daemset-nginx
......
Name:           daemset-nginx
Selector:       app=daem-nginx
Node-Selector:  <none>
......
Desired Number of Nodes Scheduled: 2
Current Number of Nodes Scheduled: 2
Number of Nodes Scheduled with Up-to-date Pods: 2
Number of Nodes Scheduled with Available Pods: 2
Number of Nodes Misscheduled: 0
Pods Status:  2 Running / 0 Waiting / 0 Succeeded / 0 Failed
......

通过上面验证查看,Node-Selector字段的值为空,表示它需要运行在集群中的每个节点之上。而当前的节点数是2个,所以其期望的Pod副本数(Desired Number of Nodes Scheduled)为2,而当前也已经创建了2个相关的Pod对象。

注意

对于特殊的硬件的节点来说,可能有的运行程序只需要在某一些节点上运行,那么通过Pod模板的spec字段中嵌套使用nodeSelector字段,并确保其值定义的标签选择器与部分特定工作节点的标签匹配即可。

更新DaemonSet对象

DaemonSet自Kubernetes1.6版本起也开始支持更新机制,相关配置嵌套在kubectl explain daemonset.spec.updateStrategy字段中。其支持RollingUpdate(滚动更新)和OnDelete(删除时更新)两种策略,滚动更新为默认的更新策略。
#(1)查看镜像版本
[root@k8s-master ~]# kubectl get pods -l app=daem-nginx -o custom-columns=NAME:metadata.name,NODE:spec.nodeName,Image:spec.containers[0].image
NAME                  NODE        Image
daemset-nginx-7s474   k8s-node1   nginx:1.12
daemset-nginx-kxpl2   k8s-node2   nginx:1.12


#(2)更新
[root@k8s-master ~]# kubectl set image daemonset/daemset-nginx nginx-pod=nginx:1.14
[root@k8s-master ~]# kubectl get pods -l app=daem-nginx -o custom-columns=NAME:metadata.name,NODE:spec.nodeName,Image:spec.containers[0].image    #再次查看
NAME                  NODE        Image
daemset-nginx-74c95   k8s-node2   nginx:1.14
daemset-nginx-nz6n9   k8s-node1   nginx:1.14

#(3)查坎详细信息
[root@k8s-master ~]# kubectl describe daemonset daemset-nginx
......
Events:
  Type    Reason            Age   From                  Message
  ----    ------            ----  ----                  -------
  Normal  SuccessfulCreate  49m   daemonset-controller  Created pod: daemset-nginx-6kzg6
  Normal  SuccessfulCreate  49m   daemonset-controller  Created pod: daemset-nginx-jjnc2
  Normal  SuccessfulDelete  40m   daemonset-controller  Deleted pod: daemset-nginx-jjnc2
  Normal  SuccessfulCreate  40m   daemonset-controller  Created pod: daemset-nginx-kxpl2
  Normal  SuccessfulDelete  40m   daemonset-controller  Deleted pod: daemset-nginx-6kzg6
  Normal  SuccessfulCreate  40m   daemonset-controller  Created pod: daemset-nginx-7s474
  Normal  SuccessfulDelete  15s   daemonset-controller  Deleted pod: daemset-nginx-7s474
  Normal  SuccessfulCreate  8s    daemonset-controller  Created pod: daemset-nginx-nz6n9
  Normal  SuccessfulDelete  5s    daemonset-controller  Deleted pod: daemset-nginx-kxpl2

通过上面查看可以看出,默认的滚动更新策略是一次删除一个工作节点上的Pod资源,待其最新版本Pod重建完成后再开始操作另一个工作节点上的Pod资源。

DaemonSet控制器的滚动更新机制也可以借助于minReadySeconds字段控制滚动节奏;必要时也可以执行暂停和继续操作。其也可以进行回滚操作。

 

 

ASP.NET Core中使用EPPlus导入出Excel文件的完整步骤

ASP.NET Core中使用EPPlus导入出Excel文件的完整步骤

前言

这篇文章说明了如何使用EPPlus在ASP.NET Core中导入和导出.xls/.xlsx文件(Excel)。在考虑使用.NET处理excel时,我们总是寻找第三方库或组件。使用Open Office Xml格式(xlsx)读取和写入Excel 2007/2010文件的最流行的.net库之一是EPPlus。这个库现在已经支持.NET Core许久了。这适用于Windows,Linux和Mac。

因此,让我们创建一个新的ASP.NET Core WEB API应用程序并安装EPPlus.Core。要安装EPPlus.Core,请在程序包管理器控制台中运行以下命令:

PM->Install-Package EPPlus.Core

或者您可以通过UI界面来安装它.

 一切就绪,现在创建一个控制器,命名为: ImportExportController ,添加后,让我们编写导出方法。

为了方便演示,我在wwwroot文件夹中创建了一个excel文件,所以我们就需要去获取我们的项目的绝对路径。

 public class ImportExportController : ControllerBase
 {
  private readonly IHostingEnvironment _hostingEnvironment;

  public ImportExportController(IHostingEnvironment hostingEnvironment)
  {
   _hostingEnvironment = hostingEnvironment;
  }
 }

 ExcelPackage 在 OfficeOpenXml 命名空间中可用的类将用于读写xlsx。定义名为“Export”的新Web api操作方法,该方法返回生成的xlsx文件的URL。所以这是将数据导出到xlsx的完整代码。其中您需要 using OfficeOpenXml;

  [HttpGet]
  public string Export()
  {
   string sWebRootFolder = _hostingEnvironment.WebRootPath;
   string sFileName = @"demo.xlsx";
   string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
   FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
   if (file.Exists)
   {
    file.Delete();
    file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
   }
   using (ExcelPackage package = new ExcelPackage(file))
   {
    // add a new worksheet to the empty workbook
    ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Employee");
    //First add the headers
    worksheet.Cells[1, 1].Value = "ID";
    worksheet.Cells[1, 2].Value = "Name";
    worksheet.Cells[1, 3].Value = "Gender";
    worksheet.Cells[1, 4].Value = "Salary (in $)";

    //Add values
    worksheet.Cells["A2"].Value = 1000;
    worksheet.Cells["B2"].Value = "Jon";
    worksheet.Cells["C2"].Value = "M";
    worksheet.Cells["D2"].Value = 5000;

    worksheet.Cells["A3"].Value = 1001;
    worksheet.Cells["B3"].Value = "Graham";
    worksheet.Cells["C3"].Value = "M";
    worksheet.Cells["D3"].Value = 10000;

    worksheet.Cells["A4"].Value = 1002;
    worksheet.Cells["B4"].Value = "Jenny";
    worksheet.Cells["C4"].Value = "F";
    worksheet.Cells["D4"].Value = 5000;

    package.Save(); //Save the workbook.
   }
   return URL;
  }

就这样。现在,当您运行此应用程序并调用export方法时。完成后,访问wwwroot您的应用程序的文件夹。您应该在系统上看到“demo.xlsx”。当你打开它时,你应该看到以下内容。

您还可以对标题进行加粗,这些并不是EPPlus.Core给我们提供的,你需要引用 using OfficeOpenXml; using OfficeOpenXml.Style;

using (var cells = worksheet.Cells[1, 1, 1, 4])
    {
     cells.Style.Font.Bold = true;
     cells.Style.Fill.PatternType = ExcelFillStyle.Solid;
     cells.Style.Fill.BackgroundColor.SetColor(Color.LightGray);
    }

 

 关于导入,其实真实的情况还是比较复杂的,我们这里就不进行验证了,对于演示,我们只是读取刚刚保存的文件。 ImportAPI 将读取文件并以格式化的字符串返回文件内容。以下是导入API的完整代码,用于读取xlsx,创建文件内容的格式化字符串并返回相同的内容。

[HttpGet]
  [Route("Import")]
  public string Import()
  {
   string sWebRootFolder = _hostingEnvironment.WebRootPath;
   string sFileName = @"demo.xlsx";
   FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
   try
   {
    using (ExcelPackage package = new ExcelPackage(file))
    {
     StringBuilder sb = new StringBuilder();
     ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
     int rowCount = worksheet.Dimension.Rows;
     int ColCount = worksheet.Dimension.Columns;
     bool bHeaderRow = true;
     for (int row = 1; row <= rowCount; row++)
     {
      for (int col = 1; col <= ColCount; col++)
      {
       if (bHeaderRow)
       {
        sb.Append(worksheet.Cells[row, col].Value.ToString() + "\t");
       }
       else
       {
        sb.Append(worksheet.Cells[row, col].Value.ToString() + "\t");
       }
      }
      sb.Append(Environment.NewLine);
     }
     return sb.ToString();
    }
   }
   catch (Exception ex)
   {
    return "Some error occured while importing." + ex.Message;
   }
  }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

您可能感兴趣的文章:
  • .Net Core使用OpenXML导出、导入Excel
  • ASP.NET Core 导入导出Excel xlsx 文件实例
  • .NET Core Dapper操作mysql数据库的实现方法
  • .net core利用orm如何操作mysql数据库详解
  • Asp.net Core 1.1 升级后操作mysql出错的解决办法
  • .Net Core导入千万级数据至Mysql数据库的实现方法

ASP.NET Core使用EPPlus操作Excel

ASP.NET Core使用EPPlus操作Excel

1.前言

  本篇文章通过ASP.NET Core的EPPlus包去操作Excel(导入导出),其使用原理与NPOI类似,导出Excel的时候不需要电脑上安装office,非常好用

2.使用

  新建一个ASP.NET Core Web应用程序(模型视图控制器),还有一个类库,SDK2.1版本,解决方案如下

 

3.在EPPlusCommon类库中创建一个EPPlusHelper类,包括两个方法,导入和读取数据

  1 using OfficeOpenXml;
  2 using OfficeOpenXml.Style;
  3 using System;
  4 using System.Collections.Generic;
  5 using System.Data;
  6 using System.Drawing;
  7 using System.IO;
  8 using System.Text;
  9 namespace EPPlusCommon
 10 {
 11     public class EPPlusHelper
 12     {
 13         private static int i;
 14 
 15         /// <summary>
 16         /// 导入数据到Excel中
 17         /// </summary>
 18         /// <param name="fileName"></param>
 19         /// <param name="ds"></param>
 20         public static bool ImportExcel(string fileName, DataSet ds)
 21         {
 22             if (ds == null || ds.Tables.Count == 0)
 23             {
 24                 return false;
 25             }
 26             FileInfo file = new FileInfo(fileName);
 27             if (file.Exists)
 28             {
 29                 file.Delete();
 30                 file = new FileInfo(fileName);
 31             }
 32             //在using语句里面我们可以创建多个worksheet,ExcelPackage后面可以传入路径参数
 33             //命名空间是using OfficeOpenXml
 34             using (ExcelPackage package = new ExcelPackage(file))
 35             {
 36                 foreach (DataTable dt in ds.Tables)
 37                 {
 38                     //创建工作表worksheet
 39                     ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(dt.TableName);
 40                     //给单元格赋值有两种方式
 41                     //worksheet.Cells[1, 1].Value = "单元格的值";直接指定行列数进行赋值
 42                     //worksheet.Cells["A1"].Value = "单元格的值";直接指定单元格进行赋值
 43                     worksheet.Cells.Style.Font.Name = "微软雅黑";
 44                     worksheet.Cells.Style.Font.Size = 12;
 45                     worksheet.Cells.Style.ShrinkToFit = true;//单元格自动适应大小
 46                     for (int i = 0; i < dt.Rows.Count; i++)
 47                     {
 48                         for (int j = 0; j < dt.Columns.Count; j++)
 49                         {
 50                             worksheet.Cells[i + 1, j + 1].Value = dt.Rows[i][j].ToString();
 51                         }
 52                     }
 53                     using (var cell = worksheet.Cells[1, 1, 1, dt.Columns.Count])
 54                     {
 55                         //设置样式:首行居中加粗背景色
 56                         cell.Style.Font.Bold = true; //加粗
 57                         cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; //水平居中
 58                         cell.Style.VerticalAlignment = ExcelVerticalAlignment.Center;     //垂直居中
 59                         cell.Style.Font.Size = 14;
 60                         cell.Style.Fill.PatternType = ExcelFillStyle.Solid;  //背景颜色
 61                         cell.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(128, 128, 128));//设置单元格背景色
 62                     }
 63                 }
 64                 //保存
 65                 package.Save();
 66             }
 67             return true;
 68         }
 69 
 70         /// <summary>
 71         /// 读取Excel数据
 72         /// </summary>
 73         /// <param name="fileName"></param>
 74         public static string ReadExcel(string fileName)
 75         {
 76             StringBuilder sb = new StringBuilder();
 77             FileInfo file = new FileInfo(fileName);
 78             try
 79             {
 80                 using (ExcelPackage package = new ExcelPackage(file))
 81                 {
 82                     var count = package.Workbook.Worksheets.Count;
 83                     for (int k = 1; k <= count; k++)  //worksheet是从1开始的
 84                     {
 85                         var workSheet = package.Workbook.Worksheets[k];
 86                         sb.Append(workSheet.Name);
 87                         sb.Append(Environment.NewLine);
 88                         int row = workSheet.Dimension.Rows;
 89                         int col = workSheet.Dimension.Columns;
 90                         for (int i = 1; i <= row; i++)
 91                         {
 92                             for (int j = 1; j <= col; j++)
 93                             {
 94                                 sb.Append(workSheet.Cells[i, j].Value.ToString() + "\t");
 95                             }
 96                             sb.Append(Environment.NewLine);
 97                         }
 98                         sb.Append(Environment.NewLine);
 99                         sb.Append(Environment.NewLine);
100                     }
101                 }
102             }
103             catch (Exception ex)
104             {
105                 return "An error had Happen";
106             }
107             return sb.ToString();
108         }
109     }
110 }

代码片段已经给出了一些注释,对于Excel的更多样式设置可以参考博客

4.新建一个ExcelController(用于读取和导入Excel),代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using EPPlusCommon;
using Microsoft.AspNetCore.Hosting;
using System.IO;
using EPPlusWeb.Models;

namespace EPPlusWeb.Controllers
{
    public class ExcelController : Controller
    {
        private readonly IHostingEnvironment _hosting;
        public ExcelController(IHostingEnvironment hosting)
        {
            _hosting = hosting;
        }
        public IActionResult Import()
        {
            string folder = _hosting.WebRootPath;
            string fileName = Path.Combine(folder, "Excel", "Test.xlsx");
            bool result = EPPlusHelper.ImportExcel(fileName, ExcelData.GetExcelData());
            string str = result ? "导入Excel成功:" + fileName : "导入失败";
            return Content(str);
        }
        public IActionResult Read()
        {
            string folder = _hosting.WebRootPath;
            string fileName = Path.Combine(folder, "Excel", "Test.xlsx");
            string result = EPPlusHelper.ReadExcel(fileName);
            return Content(result);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace EPPlusWeb.Models
{
    public class ExcelData
    {
        public static DataSet GetExcelData()
        {
            DataSet ds = new DataSet();
            string[,] infos =
            {
                { "151100310001","刘备","","计算机科学与工程学院","计算机科学与技术"},
                { "151100310002","关羽","","计算机科学与工程学院","通信工程"},
                { "151100310003","张飞","","数学与统计学院","信息与计算科学"},
                { "151100310004","小乔","","文学院","汉语言文学"}
            };
            string[,] scores =
            {
                { "151100310001","刘备","88","90","80"},
                { "151100310002","关羽","86","70","75"},
                { "151100310003","张飞","67","75","81"},
                { "151100310004","小乔","99","89","92"}
            };
            DataTable stuInfoTable = new DataTable
            {
                TableName = "学生信息表"
            };
            stuInfoTable.Columns.Add("学号", typeof(string));
            stuInfoTable.Columns.Add("姓名", typeof(string));
            stuInfoTable.Columns.Add("性别", typeof(string));
            stuInfoTable.Columns.Add("学院", typeof(string));
            stuInfoTable.Columns.Add("专业", typeof(string));
            stuInfoTable.Rows.Add("学号", "姓名", "性别", "学院", "专业");
            for (int i = 0; i < infos.GetLength(0); i++)
            {
                DataRow row = stuInfoTable.NewRow();
                for (int j = 0; j < infos.GetLength(1); j++)
                {
                    row[j] = infos[i, j];
                }
                stuInfoTable.Rows.Add(row);
            }
            ds.Tables.Add(stuInfoTable);

            DataTable stuScoreTable = new DataTable
            {
                TableName = "学生成绩表"
            };
            stuScoreTable.Columns.Add("学号", typeof(string));
            stuScoreTable.Columns.Add("姓名", typeof(string));
            stuScoreTable.Columns.Add("语文", typeof(string));
            stuScoreTable.Columns.Add("数学", typeof(string));
            stuScoreTable.Columns.Add("英语", typeof(string));
            stuScoreTable.Rows.Add("学号", "姓名", "语文", "数学", "英语");
            for (int i = 0; i < scores.GetLength(0); i++)
            {
                DataRow row = stuScoreTable.NewRow();
                for (int j = 0; j < scores.GetLength(1); j++)
                {
                    row[j] = scores[i, j];
                }
                stuScoreTable.Rows.Add(row);
            }
            ds.Tables.Add(stuScoreTable);
            return ds;
        }
    }
}

 

5.相关结果如下

 

 

 本文章代码已经放在github:https://github.com/xs0910/.NET-Core-EPPlus

ASP.NET MVC使用EPPlus,导出数据到Excel中

ASP.NET MVC使用EPPlus,导出数据到Excel中

好久没写博客了,今天特地来更新一下,今天我们要学习的是如何导出数据到Excel文件中,这里我使用的是免费开源的Epplus组件。

源代码下载:https://github.com/caofangsheng93/ExcelExportInMvc

介绍

这篇文章,介绍的是怎样导出数据到Excel文件中,大多数的后端程序都有报表功能:把显示在Grid中的数据导出到Excel文件中,这篇文章中使用的是EPPlus组件。

EPPlus是一个基于OOXML【Open Extended MarkuP Language 】格式的,操作Excel表格的类库。OOXML是由微软开发的。默认支持微软的Office。

开源网站:http://epplus.codeplex.com/

正文

上面是我们的项目。

首先我们需要引入:EPPlus。

我这里已经引入了。

当我们在程序中使用ORM的时候,我们通常将数据保存在集合中。集合中的数据不能直接导出到Excel文件中。这也就是我们为啥,需要先将List转DataTable的原因。

图1 :导出Excel的步骤

为了完成这篇文章:我们需要四个步骤

1.数据:这里我使用静态数据,来确保这篇文章尽可能通俗易懂。

2.集合:静态数据保存在集合中

3.DataTable:转化泛型集合的数据,保存到DataTable中

4.导出文件:DataTable导出为Excel

首先,我们创建一个类:

public class Student
 {
  public int ID { get; set; }
  public string Name { get; set; }
  public string Sex { get; set; }
  public int Age { get; set; }
  public string Email { get; set; }
 }
Student

然后创建一个静态数据类:

public class StaticDataOfStudent
 {
  public static List<Student> ListStudent
  {
   get 
   {
    return new List<Student>() 
    {
    new Student(){ID=1,Name="曹操",Sex="男",Email="caocao@163.com",Age=24},new Student(){ID=2,Name="李易峰",Sex="女",Email="lilingjie@sina.com.cn",new Student(){ID=3,Name="张三丰",Email="zhangsanfeng@qq.com",Age=224},new Student(){ID=4,Name="孙权",Email="sunquan@163.com",Age=1224},};
   }
  }
 }
StaticDataOfStudent

然后就是我们的导出Excel帮助类了:

/// <summary>
 /// Excel导出帮助类
 /// </summary>
 public class ExcelExportHelper
 {
  public static string ExcelContentType
  {
   get 
   {
 return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
  }
  /// <summary>
  /// List转DataTable
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="data"></param>
  /// <returns></returns>
  public static DataTable ListToDataTable<T>(List<T> data)
  {
   PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
   DataTable dataTable = new DataTable();
   for (int i = 0; i < properties.Count; i++)
   {
    PropertyDescriptor property = properties[i]; 
    dataTable.Columns.Add(property.Name,Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType); 
   }
   object[] values = new object[properties.Count];
   foreach (T item in data)
   {
    for (int i = 0; i < values.Length; i++)
    {
     values[i] = properties[i].GetValue(item);
    }
    dataTable.Rows.Add(values);
   }
   return dataTable; 
  }
  /// <summary>
  /// 导出Excel
  /// </summary>
  /// <param name="dataTable">数据源</param>
  /// <param name="heading">工作簿Worksheet</param>
  /// <param name="showSrNo">//是否显示行编号</param>
  /// <param name="columnsToTake">要导出的列</param>
  /// <returns></returns>
  public static byte[] ExportExcel(DataTable dataTable,string heading = "",bool showSrNo = false,params string[] columnsToTake)
  {
   byte[] result = null;
   using(ExcelPackage package=new ExcelPackage())
   {
    ExcelWorksheet workSheet = package.Workbook.Worksheets.Add(string.Format("{0}Data",heading));
    int startRowFrom = string.IsNullOrEmpty(heading) ? 1 : 3; //开始的行
    //是否显示行编号
    if (showSrNo)
    {
     DataColumn dataColumn = dataTable.Columns.Add("#",typeof(int));
     dataColumn.Setordinal(0);
     int index = 1;
     foreach (DaTarow item in dataTable.Rows)
     {
      item[0] = index;
      index++;
     }
    }
    //Add Content Into the Excel File
    workSheet.Cells["A" + startRowFrom].LoadFromDataTable(dataTable,true);
    // autofit width of cells with small content 
    int columnIndex = 1;
    foreach (DataColumn item in dataTable.Columns)
    {
     ExcelRange columnCells = workSheet.Cells[workSheet.Dimension.Start.Row,columnIndex,workSheet.Dimension.End.Row,columnIndex]; 
     int maxLength = columnCells.Max(cell => cell.Value.ToString().Count()); 
     if (maxLength < 150) 
     { 
      workSheet.Column(columnIndex).AutoFit(); 
     } 
     columnIndex++; 
    }
    // format header - bold,yellow on black 
    using (ExcelRange r = workSheet.Cells[startRowFrom,1,startRowFrom,dataTable.Columns.Count])
    {
     r.Style.Font.Color.SetColor(System.Drawing.Color.White);
     r.Style.Font.Bold = true;
     r.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.solid;
     r.Style.Fill.BackgroundColor.SetColor(System.Drawing.ColorTranslator.FromHtml("#1fb5ad"));
    }
    // format cells - add borders 
    using (ExcelRange r = workSheet.Cells[startRowFrom + 1,startRowFrom + dataTable.Rows.Count,dataTable.Columns.Count])
    {
     r.Style.Border.Top.Style = ExcelBorderStyle.Thin;
     r.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
     r.Style.Border.Left.Style = ExcelBorderStyle.Thin;
     r.Style.Border.Right.Style = ExcelBorderStyle.Thin;

     r.Style.Border.Top.Color.SetColor(System.Drawing.Color.Black);
     r.Style.Border.Bottom.Color.SetColor(System.Drawing.Color.Black);
     r.Style.Border.Left.Color.SetColor(System.Drawing.Color.Black);
     r.Style.Border.Right.Color.SetColor(System.Drawing.Color.Black);
    }
    // removed ignored columns 
    for (int i = dataTable.Columns.Count - 1; i >= 0; i--)
    {
     if (i == 0 && showSrNo)
     {
      continue;
     }
     if (!columnsToTake.Contains(dataTable.Columns[i].ColumnName))
     {
      workSheet.DeleteColumn(i + 1);
     }
    }
    if (!String.IsNullOrEmpty(heading))
    {
     workSheet.Cells["A1"].Value = heading;
     workSheet.Cells["A1"].Style.Font.Size = 20;

     workSheet.InsertColumn(1,1);
     workSheet.InsertRow(1,1);
     workSheet.Column(1).Width = 5;
    }
    result = package.GetAsByteArray(); 
   }
   return result;
  }
  /// <summary>
  /// 导出Excel
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="data"></param>
  /// <param name="heading"></param>
  /// <param name="isShowSlNo"></param>
  /// <param name="ColumnsToTake"></param>
  /// <returns></returns>
  public static byte[] ExportExcel<T>(List<T> data,bool isShowSlNo = false,params string[] ColumnsToTake)
  {
   return ExportExcel(ListToDataTable<T>(data),heading,isShowSlNo,ColumnsToTake); 
  }
 }

到此为止,后端服务器的代码,基本搞完,现在开始设计我们的前端代码:

我们创建一个viewmodel,用来显示数据:

public class Studentviewmodel
 {
  public List<Student> ListStudent
  {
   get 
   {
    return StaticDataOfStudent.ListStudent;
   }
  }
 }

然后创建一个控制器:

 public class HomeController : Controller
 {
  // GET: Home
  public ActionResult Index()
  {
   Studentviewmodel model = new Studentviewmodel();
   return View(model);
  }
  public FileContentResult ExportToExcel()
  {
   List<Student> lstStudent = StaticDataOfStudent.ListStudent;
   string[] columns = { "ID","Name","Age"};
   byte[] filecontent = ExcelExportHelper.ExportExcel(lstStudent,"",false,columns);
   return File(filecontent,ExcelExportHelper.ExcelContentType,"MyStudent.xlsx"); 
  }
 }

我们的视图代码:

@model ExportToExcel.Models.Studentviewmodel
@{
 ViewBag.Title = "Excel文件导出";
}
<div>
 <divhttps://www.jb51.cc/tag/heading/" target="_blank">heading">
  <a href="@Url.Action("ExportToExcel")">Export</a>
 </div>
 <div>
  <table>
   <thead>
    <tr>
     <th>ID</th>
     <th>Name</th>
     <th>Sex</th>
     <th>Age</th>
     <th>Email</th>
    </tr>
   </thead>
   <tbody>
    @foreach (var item in Model.ListStudent)
    {
     <tr>
      <td>@item.ID</td>
      <td>@item.Name</td>
      <td>@item.Sex</td>
      <td>@item.Age</td>
      <td>@item.Email</td>
     </tr>
    }
   </tbody>
  </table>
 </div>
</div>

效果图:

点击Export之后,就导出了Excel文件到浏览器中:打开之后。

总结:这个导出帮助类,可以定制导出那些列。

   string[] columns = { "ID","MyStudent.xlsx"); 

这里我只是导出这三列。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持编程小技巧!

您可能感兴趣的文章:

  • Asp.net MVC实现生成Excel并下载功能
  • asp.net 利用NPOI导出Excel通用类的方法
  • ASP.NET实现上传Excel功能
  • ASP.NET保存PDF、Word和Excel文件到数据库
  • ASP.NET Core 导入导出Excel xlsx 文件实例
  • asp.net DataTable导出Excel自定义列名的方法
  • Asp.net实现直接在浏览器预览Word、Excel、PDF、Txt文件(附源码)
  • asp.net实现导出DataTable数据到Word或者Excel的方法
  • ASP.Net动态读取Excel文件最简方法

关于asp.net – 使用vb.net和EPPlus的单元格包装vb.net imports的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于(六)Kubernetes Pod控制器-ReplicaSet和Deployment和DaemonSet、ASP.NET Core中使用EPPlus导入出Excel文件的完整步骤、ASP.NET Core使用EPPlus操作Excel、ASP.NET MVC使用EPPlus,导出数据到Excel中等相关内容,可以在本站寻找。

本文标签: