10张图带你深入理解Docker容器和镜像 - DockOne.io

mikel阅读(1101)

来源: 10张图带你深入理解Docker容器和镜像 – DockOne.io

【编者的话】本文用图文并茂的方式介绍了容器、镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker。

【Kubernetes培训通知】DockOne将会于2018年7月13日在上海举办Kubernetes技术培训,培训内容包括:Docker介绍、Docker镜像、网络、存储、容器安全;Kubernetes架构、设计理念、常用对象、网络、存储、网络隔离、服务发现与负载均衡;Kubernetes核心组件、Pod、插件、微服务、云原生、Kubernetes Operator、集群灾备等。同时深圳站培训也已经启动,欢迎咨询

这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别。

pasted_image_0.png

当我对Docker技术还是一知半解的时候,我发现理解Docker的命令非常困难。于是,我花了几周的时间来学习Docker的工作原理,更确切地说,是关于Docker统一文件系统(the union file system)的知识,然后回过头来再看Docker的命令,一切变得顺理成章,简单极了。

题外话:就我个人而言,掌握一门技术并合理使用它的最好办法就是深入理解这项技术背后的工作原理。通常情况下,一项新技术的诞生常常会伴随着媒体的大肆宣传和炒作,这使得用户很难看清技术的本质。更确切地说,新技术总是会发明一些新的术语或者隐喻词来帮助宣传,这在初期是非常有帮助的,但是这给技术的原理蒙上了一层砂纸,不利于用户在后期掌握技术的真谛。

Git就是一个很好的例子。我之前不能够很好的使用Git,于是我花了一段时间去学习Git的原理,直到这时,我才真正明白了Git的用法。我坚信只有真正理解Git内部原理的人才能够掌握这个工具。

Image Definition

镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义。

1.png

从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(译者注:运行Docker的机器)的文件系统上访问到。统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。

你可以在你的主机文件系统上找到有关这些层的文件。需要注意的是,在一个运行中的容器内部,这些层是不可见的。在我的主机上,我发现它们存在于/var/lib/docker/aufs目录下。

sudo tree -L 1 /var/lib/docker/

/var/lib/docker/
├── aufs
├── containers
├── graph
├── init
├── linkgraph.db
├── repositories-aufs
├── tmp
├── trust
└── volumes
7 directories, 2 files

 

Container Definition

容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

2.png

细心的读者可能会发现,容器的定义并没有提及容器是否在运行,没错,这是故意的。正是这个发现帮助我理解了很多困惑。

要点:容器 = 镜像 + 读写层。并且容器的定义并没有提及是否要运行容器。

接下来,我们将会讨论运行态容器。

Running Container Definition

一个运行态容器(running container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程。下面这张图片展示了一个运行中的容器。

3.png

正是文件系统隔离技术使得Docker成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层(read-write layer)。下面这张图展示了这个行为。

4.png

我们可以通过运行以下命令来验证我们上面所说的:

docker run ubuntu touch happiness.txt

即便是这个ubuntu容器不再运行,我们依旧能够在主机的文件系统上找到这个新文件。

find / -name happiness.txt

/var/lib/docker/aufs/diff/860a7b...889/happiness.txt

 

Image Layer Definition

为了将零星的数据整合起来,我们提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。

5.png

元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。

6.png

除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。

8.png

Metadata Location:
我发现在我自己的主机上,镜像层(image layer)的元数据被保存在名为”json”的文件中,比如说:

/var/lib/docker/graph/e809f156dc985.../json

e809f156dc985…就是这层的id

一个容器的元数据好像是被分成了很多文件,但或多或少能够在/var/lib/docker/containers/<id>目录下找到,<id>就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。

全局理解(Tying It All Together)

现在,让我们结合上面提到的实现细节来理解Docker的命令。

docker create <image-id>

create.jpg

docker create 命令为指定的镜像(image)添加了一个可读写层,构成了一个新的容器。注意,这个容器并没有运行。

11.png

 

docker start <container-id>

start.jpg

Docker start命令为容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。

docker run <image-id>

run.jpg

看到这个命令,读者通常会有一个疑问:docker start 和 docker run命令有什么区别。

7.png

从图片可以看出,docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节,但从另一方面来看,这容易让用户产生误解。

题外话:继续我们之前有关于Git的话题,我认为docker run命令类似于git pull命令。git pull命令就是git fetch 和 git merge两个命令的组合,同样的,docker run就是docker create和docker start两个命令的组合。

docker ps

ps.jpg

docker ps 命令会列出所有运行中的容器。这隐藏了非运行态容器的存在,如果想要找出这些容器,我们需要使用下面这个命令。

docker ps –a

ps0a.jpg

docker ps –a命令会列出所有的容器,不管是运行的,还是停止的。

docker images

images.jpg

docker images命令会列出了所有顶层(top-level)镜像。实际上,在这里我们没有办法区分一个镜像和一个只读层,所以我们提出了top-level镜像。只有创建容器时使用的镜像或者是直接pull下来的镜像能被称为顶层(top-level)镜像,并且每一个顶层镜像下面都隐藏了多个镜像层。

docker images –a

images-a.jpg

docker images –a命令列出了所有的镜像,也可以说是列出了所有的可读层。如果你想要查看某一个image-id下的所有层,可以使用docker history来查看。

docker stop <container-id>

stop.jpg

docker stop命令会向运行中的容器发送一个SIGTERM的信号,然后停止所有的进程。

docker kill <container-id>

kill.jpg

docker kill 命令向所有运行在容器中的进程发送了一个不友好的SIGKILL信号。

docker pause <container-id>

pause.jpg

docker stop和docker kill命令会发送UNIX的信号给运行中的进程,docker pause命令则不一样,它利用了cgroups的特性将运行中的进程空间暂停。具体的内部原理你可以在这里找到:https://www.kernel.org/doc/Doc … m.txt,但是这种方式的不足之处在于发送一个SIGTSTP信号对于进程来说不够简单易懂,以至于不能够让所有进程暂停。

docker rm <container-id>

rm.jpg

docker rm命令会移除构成容器的可读写层。注意,这个命令只能对非运行态容器执行。

docker rmi <image-id>

rmi.jpg

docker rmi 命令会移除构成镜像的一个只读层。你只能够使用docker rmi来移除最顶层(top level layer)(也可以说是镜像),你也可以使用-f参数来强制删除中间的只读层。

docker commit <container-id>

commit.jpg

docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。

10.png

 

docker build

build.jpg

docker build命令非常有趣,它会反复的执行多个命令。

9.png

我们从上图可以看到,build命令根据Dockerfile文件中的FROM指令获取到镜像,然后重复地1)run(create和start)、2)修改、3)commit。在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。

docker exec <running-container-id>

exec.jpg

docker exec 命令会在运行中的容器执行一个新进程。

docker inspect <container-id> or <image-id>

insepect.jpg

docker inspect命令会提取出容器或者镜像最顶层的元数据。

docker save <image-id>

save.jpg

docker save命令会创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用。和export命令不同,这个命令为每一个层都保存了它们的元数据。这个命令只能对镜像生效。

docker export <container-id>

export.jpg

docker export命令创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容(译者注:expoxt后的容器再import到Docker中,通过docker images –tree命令只能看到一个镜像;而save后的镜像则不同,它能够看到这个镜像的历史镜像)。

docker history <image-id>

history.jpg

docker history命令递归地输出指定镜像的历史镜像。

结论

我希望你们能喜欢这篇文章。还有其他许多的命令(pull,search,restart,attach等)我没有提及,但是我相信通过阅读这篇文章,大部分的Docker命令都能够被很好理解。我仅仅学习了Docker两个星期,因此,如果我有什么地方说的不好,欢迎大家指出。

原文链接:Visualizing Docker Containers and Images(翻译:杨润青)

===========================

译者介绍
杨润青,90后博士僧,研究方向是网络和信息安全。

MYSQL查询一周内的数据(最近7天的) - 追着太阳晒 - 博客园

mikel阅读(840)

来源: MYSQL查询一周内的数据(最近7天的) – 追着太阳晒 – 博客园

elect * from wap_content where week(created_at) = week(now)

如果你要严格要求是某一年的,那可以这样

查询一天:

select * from table where to_days(column_time) = to_days(now());
select * from table where date(column_time) = curdate(); 

查询一周:

select * from table  where DATE_SUB(CURDATE(), INTERVAL 7 DAY) <= date(column_time);

查询一个月:

select * from table  where DATE_SUB(CURDATE(), INTERVAL INTERVAL 1 MONTH) <= date(column_time);


每天进步一点点

ASP.NET中EVAL用法大全 - 追忆似水流年 - 博客园

mikel阅读(1056)

来源: ASP.NET中EVAL用法大全 – 追忆似水流年 – 博客园

<%# Bind(“Subject”) %> //绑定字段
<%# Container.DataItemIndex + 1%> //实现自动编号
<%# DataBinder.Eval(Container.DataItem, “[n]”) %>
通常使用的方法(这三个性能最好)
<%# DataBinder.Eval(Container.DataItem, “ColumnName”) %>
<%# DataBinder.Eval(Container.DataItem, “ColumnName”, null) %>
<%# DataBinder.Eval(Container, “DataItem.ColumnName”, null) %>
其他用法
<%# ((DataRowView)Container.DataItem)[“ColumnName”] %>
<%# ((DataRowView)Container.DataItem).Row[“ColumnName”] %>
<%# ((DataRowView)Container.DataItem)[“adtitle”] %>
<%# ((DataRowView)Container.DataItem)[n] %>
<%# ((DbDataRecord)Container.DataItem)[0] %>
<%# (((自定义类型)Container.DataItem)).属性.ToString() %>//如果属性为字符串类型就不用ToString()了
DataBinder.Eval用法范例
<%# DataBinder.Eval(Container.DataItem, “IntegerValue”, “{0:c}”) %>
格式化字符串参数是可选的。如果忽略参数,DataBinder.Eval 返回对象类型的值,

//显示二位小数
<%# DataBinder.Eval(Container.DataItem, “UnitPrice”, “${0:F2}”) %>

//{0:G}代表显示True或False
<ItemTemplate>
<asp:Image Width=”12″ Height=”12″ Border=”0″ runat=”server”
AlternateText='<%# DataBinder.Eval(Container.DataItem, “Discontinued”, “{0:G}”) %>’
ImageUrl='<%# DataBinder.Eval(Container.DataItem, “Discontinued”, “~/images/{0:G}.gif”) %>’ />
</ItemTemplate>

//转换类型
((string)DataBinder.Eval(Container, “DataItem.P_SHIP_TIME_SBM8”)).Substring(4,4)
{0:d} 日期只显示年月日
{0:yyyy-mm-dd} 按格式显示年月日
{0:c} 货币样式
<%#Container.DataItem(“price”,”{0:¥#,##0.00}”)%>
<%# DataBinder.Eval(Container.DataItem,”Company_Ureg_Date”,”{0:yyyy-M-d}”)%>
Specifier Type      Format    Output (Passed Double 1.42)   Output (Passed Int -12400)
c   Currency         {0:c}      $1.42      -$12,400
d   Decimal          {0:d}     System.FormatException   -12400
e   Scientific       {0:e}     1.420000e+000     -1.240000e+004
f   Fixed point      {0:f}   1.42     -12400.00
g   General          {0:g}   1.42      -12400
n   Number with commas for thousands   {0:n}   1.42      -12,400
r   Round trippable     {0:r}   1.42      System.FormatException
x   Hexadecimal     {0:x4}   System.FormatException    cf90

{0:d} 日期只显示年月日
{0:yyyy-mm-dd} 按格式显示年月日

样式取决于 Web.config 中的设置
{0:c}   或 {0:£0,000.00} 货币样式   标准英国货币样式
<system.web>
<globalization requestEncoding=”utf-8″ responseEncoding=”utf-8″ culture=”en-US” uiCulture=”en-US” />
</system.web>
显示为 £3,000.10

{0:c}   或 string.Format(“{0:C}”, price); 中国货币样式
<system.web>
<globalization requestEncoding=”utf-8″ responseEncoding=”utf-8″ culture=”zh-cn” uiCulture=”zh-cn” />
</system.web>
显示为 ¥3,000.10

{0:c}   或 string.Format(“{0:C}”, price); 美国货币样式
<system.web>
<globalization requestEncoding=”utf-8″ responseEncoding=”utf-8″ />
</system.web>
显示为 $3,000.10

转自:ASP.NET中EVAL用法大全

[二合一]C#读取和导出EXCEL类库(可操作多个SHEET) - woody.wu - 博客园

mikel阅读(1396)

来源: [二合一]C#读取和导出EXCEL类库(可操作多个SHEET) – woody.wu – 博客园

改进的一个EXCEL类,可以读取EXCEL,也可以将内容导出到EXCEL.操作多个SHEET比较方便.
而且无须安装任何EXCEL.
1.读取.


public   void Read(string FileName)
{
AppLibrary.ReadExcel.Workbook workbook = null;
workbook = AppLibrary.ReadExcel.Workbook.getWorkbook(FileName);
int k = 0;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (AppLibrary.ReadExcel.Sheet sheet in workbook.Sheets)
{
k++;
sb.Append(“当前为第” + k + “个Sheet<br/><br/>”);
sb.Append(“<table class=’scroll’ cellpadding=0 cellspacing=0>”);
for (int iRow = 0; iRow < sheet.Rows; iRow++)
{
sb.Append(“<tr>”);
sb.Append(“<td>”);
sb.Append(“当前为第” + (iRow + 1) + “行”);
sb.Append(“</td>”);
for (int iCol = 0; iCol < sheet.Columns; iCol++)
{
AppLibrary.ReadExcel.Cell cell = sheet.getCell(iCol, iRow);
object val = cell.Value;

string cellValue = (val != null) ? val.ToString() : “”;

sb.Append(“<td>”);
sb.Append(cellValue);
sb.Append(“</td>”);

}
sb.Append(“</tr>”);
}
sb.Append(“</table><br/><br/>”);
}
Response.Write(sb.ToString());

}

 

读取的EXCEL:

读取后:

 

导出:


public void ToExcel()
{
AppLibrary.WriteExcel.XlsDocument doc = new AppLibrary.WriteExcel.XlsDocument();
doc.FileName = “Report.xls”;
string SheetName = string.Empty;
//记录条数
int mCount = 55;
//每个SHEET的数量
int inv = 10;
//计算当前多少个SHEET
int k = Convert.ToInt32(Math.Round(Convert.ToDouble(mCount / inv))) + 1;

for (int i = 0; i < k; i++)
{
SheetName = “当前是SHEET” + i.ToString();
AppLibrary.WriteExcel.Worksheet sheet = doc.Workbook.Worksheets.Add(SheetName);
AppLibrary.WriteExcel.Cells cells = sheet.Cells;
//第一行表头
cells.Add(1, 1, “序号”);
cells.Add(1, 2, “准考证号”);
cells.Add(1, 3, “考生姓名”);
cells.Add(1, 4, “性别”);
cells.Add(1, 5, “报考专业”);
int f = 1;
for (int m = i * inv; m < mCount && m < (i + 1) * inv; m++)
{
f++;
cells.Add(f, 1, f – 1);
cells.Add(f, 2, f);
cells.Add(f, 3, “woody.wu”);
cells.Add(f, 4, “男”);
cells.Add(f, 5, “经济学”);
}
}
doc.Send();
Response.Flush();
Response.End();
}

 

 

演示代码
类库源码

C# Excel导入、导出【源码下载】 - 方木一 - 博客园

mikel阅读(948)

来源: C# Excel导入、导出【源码下载】 – 方木一 – 博客园

本篇主要介绍C#的Excel导入、导出。

目录

1. 介绍:描述第三方类库NPOI以及Excel结构

2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图、NOPI以及C#代码

3. Excel导出:介绍C#如何调用NPOI进行Excel导出,包含:流程图、NOPI以、C#代码以及代码分析

4. 源码下载:展示运行图及源码下载

 

1. 介绍

1.1 第三方类库:NPOI

说明:NPOI是POI项目的.NET 版本,可用于Excel、Word的读写操作。

优点:不用装Office环境。

下载地址:http://npoi.codeplex.com/releases

 

1.2 Excel结构介绍

工作簿(Workbook):每个Excel文件可理解为一个工作簿。

工作表(Sheet):一个工作簿(Workbook)可以包含多个工作表。

行(row):一个工作表(Sheet)可以包含多个行。

 

2. Excel导入

2.1 操作流程

 

2.2 NPOI操作代码

说明:把Excel文件转换为List<T>

步骤:

①读取Excel文件并以此初始化一个工作簿(Workbook);

②从工作簿上获取一个工作表(Sheet);默认为工作薄的第一个工作表;

③遍历工作表所有的行(row);默认从第二行开始遍历,第一行(序号0)为单元格头部;

④遍历行的每一个单元格(cell),根据一定的规律赋值给对象的属性。

代码:

 

2.3 C#逻辑操作代码

说明:对Excel转换后的List<T>进行后续操作;如:检测有效性、持久化存储等等

步骤:

①调用2.2代码,把Excel文件转换为List<T>。

②对List<T>进行有效性检测:必填项是否为空、是否有重复记录等等。

③对List<T>进行持久化存储操作。如:存储到数据库。

④返回操作结果。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public void ImportExcel(HttpContext context)
{
    StringBuilder errorMsg = new StringBuilder(); // 错误信息
    try
    {
        #region 1.获取Excel文件并转换为一个List集合
        // 1.1存放Excel文件到本地服务器
        HttpPostedFile filePost = context.Request.Files["filed"]; // 获取上传的文件
        string filePath = ExcelHelper.SaveExcelFile(filePost); // 保存文件并获取文件路径
        // 单元格抬头
        // key:实体对象属性名称,可通过反射获取值
        // value:属性对应的中文注解
        Dictionary<stringstring> cellheader = new Dictionary<stringstring> {
            "Name""姓名" },
            "Age""年龄" },
            "GenderName""性别" },
            "TranscriptsEn.ChineseScores""语文成绩" },
            "TranscriptsEn.MathScores""数学成绩" },
        };
        // 1.2解析文件,存放到一个List集合里
        List<UserEntity> enlist = ExcelHelper.ExcelToEntityList<UserEntity>(cellheader, filePath, out errorMsg);
        #endregion
        #region 2.对List集合进行有效性校验
        #region 2.1检测必填项是否必填
        for (int i = 0; i < enlist.Count; i++)
        {
            UserEntity en = enlist[i];
            string errorMsgStr = "第" + (i + 1) + "行数据检测异常:";
            bool isHaveNoInputValue = false// 是否含有未输入项
            if (string.IsNullOrEmpty(en.Name))
            {
                errorMsgStr += "姓名列不能为空;";
                isHaveNoInputValue = true;
            }
            if (isHaveNoInputValue) // 若必填项有值未填
            {
                en.IsExcelVaildateOK = false;
                errorMsg.AppendLine(errorMsgStr);
            }
        }
        #endregion
        #region 2.2检测Excel中是否有重复对象
        for (int i = 0; i < enlist.Count; i++)
        {
            UserEntity enA = enlist[i];
            if (enA.IsExcelVaildateOK == false// 上面验证不通过,不进行此步验证
            {
                continue;
            }
            for (int j = i + 1; j < enlist.Count; j++)
            {
                UserEntity enB = enlist[j];
                // 判断必填列是否全部重复
                if (enA.Name == enB.Name)
                {
                    enA.IsExcelVaildateOK = false;
                    enB.IsExcelVaildateOK = false;
                    errorMsg.AppendLine("第" + (i + 1) + "行与第" + (j + 1) + "行的必填列重复了");
                }
            }
        }
        #endregion
        // TODO:其他检测
        #endregion
        // 3.TODO:对List集合持久化存储操作。如:存储到数据库
        
        // 4.返回操作结果
        bool isSuccess = false;
        if (errorMsg.Length == 0)
        {
            isSuccess = true// 若错误信息成都为空,表示无错误信息
        }
        var rs = new { success = isSuccess,  msg = errorMsg.ToString(), data = enlist };
        System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer();
        context.Response.ContentType = "text/plain";
        context.Response.Write(js.Serialize(rs)); // 返回Json格式的内容
    }
    catch (Exception ex)
    {
     throw ex;
    }
}

 

3. Excel导出

3.1 导出流程

 

3.2 NPOI操作代码

说明:把List<T>转换为Excel

步骤:

①创建一个工作簿(Workbook);

②在工作簿上创建一个工作表(Sheet);

③在工作表上创建第一行(row),第一行为列头,依次写入cellHeard的值(做为列名)。

④循环遍历List<T>集合,每循环一遍创建一个行(row),然后根据cellHeard的键(属性名称)依次从List<T>中的实体对象取值存放到单元格内。

代码:

 

3.3 C#逻辑操作代码

说明:对Excel转换后的List<T>进行后续操作;如:检测有效性、持久化存储等等

步骤:

①获取List<T>集合。

②调用3.2,将List<T>转换为Excel文件。

③服务器存储Excel文件并返回下载链接。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void ExportExcel(HttpContext context)
{
    try
    {
        // 1.获取数据集合
        List<UserEntity> enlist = new List<UserEntity>() {
            new UserEntity{Name="刘一",Age=22,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=80,MathScores=90}},
            new UserEntity{Name="陈二",Age=23,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=81,MathScores=91} },
            new UserEntity{Name="张三",Age=24,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=82,MathScores=92} },
            new UserEntity{Name="李四",Age=25,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=83,MathScores=93} },
            new UserEntity{Name="王五",Age=26,Gender="Male",TranscriptsEn=new TranscriptsEntity{ChineseScores=84,MathScores=94} },
        };
        // 2.设置单元格抬头
        // key:实体对象属性名称,可通过反射获取值
        // value:Excel列的名称
        Dictionary<stringstring> cellheader = new Dictionary<stringstring> {
            "Name""姓名" },
            "Age""年龄" },
            "GenderName""性别" },
            "TranscriptsEn.ChineseScores""语文成绩" },
            "TranscriptsEn.MathScores""数学成绩" },
        };
        // 3.进行Excel转换操作,并返回转换的文件下载链接
        string urlPath = ExcelHelper.EntityListToExcel2003(cellheader, enlist, "学生成绩");
        System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer();
        context.Response.ContentType = "text/plain";
        context.Response.Write(js.Serialize(urlPath)); // 返回Json格式的内容
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

 

3.4 代码分析

核心代码主要是cellheader与List<T>之间的映射关系:

 

4. 源码下载

4.1 运行图

 

4.2 下载地址

百度网盘: http://pan.baidu.com/s/1o69We8M

CSDN:http://download.csdn.net/download/polk6/8974195

GitHub版本控制使用(命令行实际操作图解Git使用方法) - CSDN博客

mikel阅读(926)

来源: GitHub版本控制使用(命令行实际操作图解Git使用方法) – CSDN博客

Git是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目

Git的优势: (说到优势,那么自然是相对与SVN而言的)

  1. 版本库本地化,支持离线提交,相对独立不影响协同开发。每个开发者都拥有自己的版本控制库,在自己的版本库上可以任意的执行提交代码、创建分支等行为。例如,开发者认为自己提交的代码有问题?没关系,因为版本库是自己的,回滚历史、反复提交、归并分支并不会影响到其他开发者。
  2. 更少的“仓库污染”。git对于每个工程只会产生一个.git目录,这个工程所有的版本控制信息都在这个目录中,不会像SVN那样在每个目录下都产生.svn目录。
  3. 把内容按元数据方式存储,完整克隆版本库。所有版本信息位于.git目录中,它是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签、分支、版本记录等。
  4. 支持快速切换分支方便合并,比较合并性能好。在同一目录下即可切换不同的分支,方便合并,且合并文件速度比SVN快。
  5. 分布式版本库,无单点故障,内容完整性好。内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

Git的安装方式在windows下安装需要下载一个git的软件,在linux下相对方便,只需要一条指令,Git安装后的配置网上也有一堆的教程,就不详细说了。

下面说下Git的基本使用:

一、建立远程仓库:
要使用git,首先得有一个远程仓库吧,下面是在自己的主页中新建一个远程仓库:
ps:其中仓库类型要选择public(公开),私有的是要收费的
这里写图片描述
这里写图片描述

建立远程仓库之后会有这样一个页面,点击箭头所指按钮,拷贝仓库地址: (这个地址一会要用)
这里写图片描述

通过命令 git init 把当前目录变成git可以管理的仓库:
首先创建一个文件,文件内容为111111
这里写图片描述

二、基本的推送功能:
使用 git init 指令,这时候你当前 TestGit 目录下会多了一个 .git 的目录,这个目录是Git来跟踪管理版本的。 .git 文件夹里面的任何文件都不能动
这里写图片描述

首先使用 add 指令,将当前更改或者新增的文件加入到Git的索引中,加入到Git的索引中就表示记入了版本历史中,这也是提交之前所需要执行的一步,(在第一条指令前少了一个 git add file 命令)
其次使用 commit 指令,提交当前工作空间的修改内容到本地版本库
然后 remote 把本地版本库与远程仓库建立联系
最后使用 push 指令,把本地版本推送到远端仓库
这里写图片描述

接下来使用 status 命令,查看本地提交情况,(下面是没有需要提交的)
然后在文件的最后追加一行 222222
使用 diff 指令,查看之前的文件与现在的文件有什么改变(文件增加了一行内容)
这里写图片描述

再次使用status指令查看提交状态,发现有一个文件做了修改,并且没有添加到Git索引中(因为状态是红色的)
接着使用三步,再次把修改提交,并推送到远端仓库
这里写图片描述

三、版本回退:
按照上面的步骤我们再在文件最后增加一行 “333333”;至此,我们已经做了三次修改,并且都推送到远端仓库中了,下面的图可以看出
这里写图片描述

现在我已经对文件file做了三次修改了,那么我现在想查看下历史记录,如何查呢?我们现在可以使用命令 log, 演示如下所示:
这里写图片描述

精简的日志信息,可以使用 git log –pretty=oneline 指令
这里写图片描述

现在我发现刚提交的添加的333333有问题,我想回到添加333333之前的状态,也就是只有 111111 222222这两行的状态,可以使用 reset 指令
这里写图片描述

再次查看提交日志,发现提交333333 的日志没了,这个时候 file 中已经没有333333了
这里写图片描述

但是现在的问题假如我想回到只有111111的状态,可以通过 git reset –hard HEAD^^,这不是很笨吗?或者
我发现之前的333333是有用的,我有怎么回到有333333的状态呢?
这时候可以通过 reflog 指令获得每次提交的版本号,演示如下:
这里写图片描述

git reset –hard 版本号 ,回退到任意一次提交状态
这里写图片描述

下面是 log 与 reflog 的区别:可以看到 log 只能查看 commit(提交)记录,并不是所有记录,而 reflog 指令可以查看版本库的任何记录,包括回退记录
这里写图片描述

如果在 commit 之前,想要返回之前的状态,可以使用checkout指令
具体用法如下:
1、例如在file后面追加一行444444, file被修改后,还没有使用add命令添加到Git索引中
这里写图片描述

使用 git checkout – file (file是想要回退的文件,可以换成其他的) 把修改除去。
这里写图片描述

2、再次在 file 尾部添加 444444, 并且使用了 git add 之后
这里写图片描述

checkout 之后,修改并没有消失
这里写图片描述

再次添加 555555
这里写图片描述
这里写图片描述

没办法了,只有提交了
这里写图片描述

提交之后还可以回退到没有 444444 的版本:
这里写图片描述
这里写图片描述
当然除了这一种办法,还可以在 commit (提交)之前,自己在file中把 444444 删除了,然后再 commit 一次就可以了。

四、删除与恢复功能:
新建一个 test 文件,把test推送到远程仓库中
这里写图片描述

新增了一个文件 test :
这里写图片描述

可以在工作区中直接删除 test 来测试:
这里写图片描述

使用 git checkout – test(已删除的文件名)来回复test:
这里写图片描述

再试试另一种不能恢复的删除方式:(删除之后提交一次,就彻底地把 test 从版本库中删除了)
这里写图片描述

五、下载功能:
在任意一个目录下(即使没有之前的 .git 版本库),可以使用 clone 指令,克隆任意一个存在的远程仓库到本地
这里写图片描述

下面大致总结一下上面讲到的指令:

git init 当前的目录变成可以管理的git仓库,生成隐藏.git文件。
git add XX 把xx文件添加到暂存区去(XX可以使 单独的一个 . 表示添加工作区所有文件)
git commit –m “XX” 提交文件 XX是这次提交的注释。
git status 查看仓库状态
git diff XX 查看XX文件修改了那些内容
git log 查看历史记录
git reset –hard HEAD^ 或者 git reset –hard HEAD~ 回退到上一个版本
(如果想回退到100个版本,使用git reset –hard HEAD~100 )
git reflog 查看历史记录的版本号
git checkout – XX 把XX文件在工作区的修改全部撤销。
git rm XX 删除XX文件
git remote add origin https://github.com/用户id/远程仓库名.git 关联一个远程库
git push –u(第一次要用-u 以后不需要) origin master 把当前master分支推送到远程库
git clone https://github.com/用户id/远程仓库名.git 从远程库中克隆到本地

Git的使用还用其他很多方面,这里只是列举了一些常用的指令,其他的指令我也在学习中,如果有什么不对的地方,欢迎大神留言指正,非常感激。。

Asp.NET MVC 导入Excel数据教程 手把手教你系列!!! http://www.cnblogs.com/Lxy-/p/5791721.html - 高达 - 博客园

mikel阅读(845)

来源: Asp.NET MVC 导入Excel数据教程 手把手教你系列!!! http://www.cnblogs.com/Lxy-/p/5791721.html – 高达 – 博客园

复制代码
复制代码
@section scripts{
    <script type="text/javascript">
        $('input[id=fileUpload]').change(function () {
            $('#txt_Path').val($(this).val());
            $('#form_Upload').submit();
        });
    </script>}
复制代码
复制代码

 

 

这段JS代码功能就是在<input id=”fileUpload” type=”file” name=”file” style=”display:none”>选择文件后就把文件路径赋给我们自己构造的文本框里,然后就是提交表单,上传它的选择的文件到我们的服务器。

请注意灰色那段代码是如何用我们自己构建的按钮绑定到fileUpload的功能上

 

1  @*浏览本地文件按钮*@
2     <span id="btn_Browse" style="cursor:pointer;" onclick="$('input[id=fileUpload]').click();" class="input-group-addon">
3         <i class="glyphicon glyphicon-folder-open"></i>&nbsp;&nbsp;&nbsp;浏览文件
4     </span>

 

现在就来测试下我们写的代码能不能跑!F5运行!如果是以下效果就是正确的

 

 

 

为了浏览文件有响应的效果,我们在视图页加一小段CSS代码

复制代码
复制代码
 1 @section scripts{
 2 
 3 
 4     <style type="text/css">
 5         #btn_Browse:hover {
 6             color: #3C763D;
 7         }
 8     </style>
 9 
10     <script type="text/javascript">
11         $('input[id=fileUpload]').change(function () {
12             $('#txt_Path').val($(this).val());
13             $('#form_Upload').submit();
14         });
15 
16 
17     
18     </script>}
复制代码
复制代码

 

第五步

到了这里我们的文件已经可以上传到我们服务了,如果不信你在Browse操作打个断点,看看file参数是不是已经接受了文件,如果接受到了说明已经成功一半了!我们还是先不写Browse操作处理Excel文件的代码,焦点还是在视图页上,在本博客第一张效果图里,大家看到浏览文件下面有张table表格吗?小弟创建这个表格只是为了更好的交互效果,让使用的人更直观而已。而且也很简单!

接下来我们来构建它,在视图页新增table表格的代码

 

复制代码
复制代码
@model Student
@using School.Entity


<table class="table  table-striped  table-hover table-bordered">
    <tr>
        <th>@Html.DisplayNameFor(model => model.Name)</th>
        <th>@Html.DisplayNameFor(model => model.Age)</th>
        <th>@Html.DisplayNameFor(model => model.ChineseScore)</th>
        <th>@Html.DisplayNameFor(model => model.EnglishScore)</th>
        <th>@Html.DisplayNameFor(model => model.MathScore)</th>
    </tr>

    @if (ViewBag.Data != null)
    {
        //生成前10条数据 填充表格table
        foreach (var item in (ViewBag.Data as IEnumerable<Student>).Take(10))
        {
            <tr>
                <td>@Html.DisplayFor(model => item.Name)</td>
                <td>@Html.DisplayFor(model => item.Age)</td>
                <td>@Html.DisplayFor(model => item.ChineseScore)</td>
                <td>@Html.DisplayFor(model => item.EnglishScore)</td>
                <td>@Html.DisplayFor(model => item.MathScore)</td>
            </tr>
        }
    }
</table>
<h5 class="text-info">
    默认显示前10条记录
</h5>
复制代码
复制代码

在这里我依然用的@HTML辅助方法,如果你还不会使用它,赶紧花一天学习它,入门非常简单!非常强大! 设想如果没有@Html扩展方法 小弟得写多少硬编码啊!

 

然后再次按F5运行先看看效果

 

 

这里的表头是英文的,如果你想变成中文的话,可以在实体上加上数据注解特性(如下)

 

复制代码
复制代码
1 public class Student
2   {
3 
4         [Display(Name="中文成绩")]
5         public int ChineseScore { get; set; }
6 
7    }
复制代码
复制代码

 

 

 

对了还忘了一个东西,就是上传提交按钮,我们现在来构建它!在视图页form表单下面添加如下代码

 

复制代码
复制代码
1 @using (Html.BeginForm("Browse", "UploadExcel", FormMethod.Post, new { enctype = "multipart/form-data", id = "form_Upload" }))
2 {
3     @Html.AntiForgeryToken()
4     <input id="fileUpload" type="file" name="file" style="display:none"> 
5 }
6 
//红色部分是我构建的上传提交按钮
7 <div class="input-group pull-right" style="margin:0 0 5px 0">
8     @Html.RouteLink("开始提交", new { action = "Upload" }, new { id="submit", @class = "btn  btn-primary ladda-button ", data_style = "expand-right" })
9 </div>
复制代码
复制代码

 

 

 

@Html.RouteLink扩展方法会根据我定义的路由生成一个<a>锚标签,最后生成如下html标记

<a id=”submit” class=”btn  btn-primary ladda-button” data-style=”expand-right” href=”/UploadExcel/Upload” >开始提交</a>

 

在这里我把它伪装成了一个button按钮

 

 

data-style=”expand-right”这些属性是我用bootstrap加了个5毛钱的特效,你也可以不用管,也可以使用自己的特效。这个上传提交按钮的功能就是最后一个功能,把经过Browse操作转换成List<T>的数  据导入到我们的数据库。到现在为止我们的导入Excel的页面已经全部完成了,当然我的审美观和前端技术就是渣渣,所以请原谅小弟! href=”/UploadExcel/Upload” 这个<a>锚标签会访问UploadExcelController控制器的Upload操作,所以我再添加最后一个操作。在控制器添加如下代码

 

复制代码
复制代码
 public class UploadExcelController : Controller
    {
        // GET: /UploadExcel
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [HandleError(View = "~/Views/Shared/Error.cshtml")]
        public ActionResult Browse(HttpPostedFileBase file)
        {

            return null;
        }

        //红色部分是我新增的Action操作,这个操作的作用是把Browse操作转换好的List<T> 通过业务服务层 导入我们数据库
        [HandleError(View = "~/Views/Shared/Error.cshtml")]
        public ActionResult Upload()
        {
            return View("UploadSuccess"); //导入成功的页面 这个页面就留给大家自己设计吧
        }

      }
复制代码
复制代码

 

 

现在我们来检查下我们构造好的页面,F5运行。如果是下面的样子我们就全部完成视图页面了

 

 

现在我们把重点放在Excel文件的逻辑处理上了,我们先从Browse操作下手,因为此操作负责把我们上传的Excel文件转换成List Entity对象,只要转换成这个集合对象你后面就可以想怎么插入就怎么插入了 !想插入MSSQL MYSQL 等不同数据都可以呵呵!因为我们用的ORM框架!

按照我上传的那个思维图,我想我先处理验证!先判断文件的格式是不是Excel的格式。(Excel的格式是根据版本来的 2007-2010 是xlsx,2003是xls)这里我只默认了2007-2010 。

在Browse操作添加如下代码

 

复制代码
复制代码
 1         [HttpPost]
 2         [ValidateAntiForgeryToken]
 3         [HandleError(View = "~/Views/Shared/Error.cshtml")]
 4         public ActionResult Browse(HttpPostedFileBase file)
 5         {
 6 
 7             if (string.Empty.Equals(file.FileName) || ".xlsx" != Path.GetExtension(file.FileName))
 8             {
 9                 throw new ArgumentException("当前文件格式不正确,请确保正确的Excel文件格式!");
10             }
11 
12             var severPath = this.Server.MapPath("/files/"); //获取当前虚拟文件路径
13 
14             var savePath = Path.Combine(severPath, file.FileName); //拼接保存文件路径
15 
16             try
17             {
18                 file.SaveAs(savePath);
19                 stus = ExcelHelper.ReadExcelToEntityList<Student>(savePath);
20                 ViewBag.Data = stus;
21                 return View("Index");
22             }
23             finally
24             {
25                 System.IO.File.Delete(savePath);//每次上传完毕删除文件
26             }
27 
28         }
复制代码
复制代码

 

我在MVC项目的根目录创建了个files的文件夹,用来保存上传的Excel文件。然后读取文件转换成List Entity对象,然后把它传给我们创建的视图。这样就可以一选择Excel文件就把数据显示在页面上了,转换数据的核心是这句代码

stus = ExcelHelper.ReadExcelToEntityList<Student>(savePath);

ExcelHelper是我自己封装的一个工具库,我现在来创建它。在根目录添加一个文件夹,然后添加一个类

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
ppublic class ExcelHelper
   {
       //Excel数据转List<T>
       public static IList<T> ReadExcelToEntityList<T>(string filePath) where T : classnew()
       {
           DataTable tbl = ReadExcelToDataTable(filePath);//读取Excel数据到DataTable
           IList<T> list = DataTableToList<T>(tbl);
           return list;
       }
       //Excel数据转DataTable 使用的oledb读取方式
       public static DataTable ReadExcelToDataTable(string filePath)
       {
           if (filePath == string.Empty) throw new ArgumentNullException("路径参数不能为空");
           string ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Persist Security Info=False;Data Source=" + filePath + "; Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
           OleDbDataAdapter adapter = new OleDbDataAdapter("select * From[Sheet1$]", ConnectionString); //默认读取的Sheet1,你也可以把它封装变量,动态读取你的Sheet工作表
           DataTable table = new DataTable("TempTable");
           adapter.Fill(table);
           return table;
       }
       //DataTable转List<T>
       public static List<T> DataTableToList<T>(DataTable dt) where T : classnew()
       {
           if (dt == nullreturn null;
           List<T> list = new List<T>();
           //遍历DataTable中所有的数据行 
           foreach (DataRow dr in dt.Rows)
           {
               T t = new T();
               PropertyInfo[] propertys = t.GetType().GetProperties();
               foreach (PropertyInfo pro in propertys)
               {
                   //检查DataTable是否包含此列(列名==对象的属性名)   
                   if (dt.Columns.Contains(pro.Name))
                   {
                       object value = dr[pro.Name];
                       value = Convert.ChangeType(value, pro.PropertyType);//强制转换类型
                       //如果非空,则赋给对象的属性  PropertyInfo
                       if (value != DBNull.Value)
                       {
                           pro.SetValue(t, value, null);
                       }
                   }
               }
               //对象添加到泛型集合中 
               list.Add(t);
           }
           return list;
       }
   }

 

 

代码很简单我就不翻译了,就是读取Excel数据转各种C#对象,但是这是教学代码不是产品代码,我很粗暴的封装了。你如果要用到生成环境,得还要加上各种逻辑验证和测试!

好了到了这步,我们就可以出现下面的效果了:

 

写到这里,感觉最后一个功能把List<T>集合导入数据库大家应该都会,我就不想再继续往下写了。但是还是要说下注意的地方就是导入数据一定要支持事物回滚功能,就是哪怕前面已经导入了几十条数据了,如果发生一条脏数据导致插入异常,也必须回滚判定全部导入失败。避免重复导入,导致数据库脏数据。

我贴最后导入数据库的代码就是Upload操作,我在DAL层是使用了事物处理的,支持回滚!

 

复制代码
复制代码
        [HandleError(View = "~/Views/Shared/Error.cshtml")]
        public ActionResult Upload()
        {
            var result= Ioc.Service.IocBll<IStudentBll>.Provide.Insert(stus);

            if(string.Empty!=result.Success)  
                ViewBag.Info = result.Info;

            return View("UploadSuccess");

        }
复制代码
复制代码

 

苹果笔记本开机出现闪烁的问号文件夹解决方法 - CSDN博客

mikel阅读(4648)

来源: 苹果笔记本开机出现闪烁的问号文件夹解决方法 – CSDN博客

苹果笔记本开机出现闪烁的问号文件夹解决方法 如果出现这种情况,这就意味着你的Mac无法找到自身的系统软件*

这里写图片描述
这个问题有两种情况:
**

一、 如果问号只出现几秒钟,然后继续启动。

**
这时候需要我们进入电脑找到“系统偏好设置”中重新选择你的启动磁盘。
这里写图片描述
1. 从苹果菜单中选取“系统偏好设置”,然后点按“启动磁盘”。
2. 点按您通常用于启动电脑的磁盘图标。通常名为“Macintosh HD”。
3. 关闭“系统偏好设置”窗口。
Mac 现在应能正常启动,而不再出现闪烁的问号。

二、 问号文件夹一直闪烁,无法启动Mac

  1. 通过按住其电源按钮几秒钟来关闭 Mac。(即强制关机啦)
  2. 按一下电源按钮,以重新打开 Mac。立即按住键盘上的 Command 和 R 键或者Option键,以便从 macOS 恢复功能启动您的 Mac。一直按住,直到看到 Apple 标志或地球图标。(据小编经验出现的都是地球图标)
  3. 在启动过程中,如果出现提示,请选择一个 Wi-Fi 网络来连接互联网。(必须保持网络处于连接状态)
  4. 在“macOS 实用工具(从地球图标进到此处取决于你的网络速度了,最好找个网络好的连接)”窗口出现后,从苹果菜单中选取“启动磁盘”。
  5. 选择启动磁盘,然后点按“重新启动”
    如果没有看到您的启动磁盘
    如果您没有在“启动磁盘”窗口中看到您的启动磁盘,请按照以下步骤操作以查看您的启动磁盘是否需要目录修复。
  6. 从macOS 实用工具窗口中打开磁盘工具。
  7. 在“磁盘工具”窗口中,从窗口的左侧选择您的启动磁盘(通常名为“Macintosh HD”)。
    这里写图片描述
  8. 点按“急救”标签页。
  9. 点按“修复磁盘”按钮以验证并修复任何与您的启动磁盘有关的问题。
  10. 您的磁盘成功修复后,退出“磁盘工具”。
  11. 从苹果菜单中选取“启动磁盘”。选择启动磁盘,然后点按“重新启动”。
    如果按重新启动后,出现如下问题:
    这里写图片描述

那就出最后的杀手锏了(重装系统),用TransMac在 Windows系统下制作 OS X启动盘(类似于我们Windows的制作一个PE)

需要的工具有:win电脑一台,8G以上的U盘
(1)、首先下载软件TransMac 11下载地址:链接:http://pan.baidu.com/s/1nvS4ASh 密码:6xvi 安装软件前先看安装说明。
(2)、下载苹果系统镜像。
注1:苹果镜像一定要下载下列版本(镜像都论坛大神制作的为了大家不走弯路下错镜像特此提供常见版本下载链接)
注2:下列镜像文件只能保证在白苹果机器上成功安装,黑苹果请绕行。。谢谢。。
镜像下载地址:
10.8.5 百度云 http://pan.baidu.com/s/1jIAZJcM 密码:1s96
10.9.5 百度云 http://pan.baidu.com/s/1bp0yEBX 密码:4k0n
10.10.5 百度云 http://pan.baidu.com/s/1jH8bJoi 密码:yrhw
10.11.6 百度云 https://pan.baidu.com/s/1qYGOX9i 密码:rtsn
10.12 百度云 http://pan.baidu.com/s/1dE75UTV 密码:hfn4
10.12.1 百度云 http://pan.baidu.com/s/1ccMab8 密码: kbw9
10.12.2 百度云 http://pan.baidu.com/s/1skGDLCh 密码: bbr4
10.12.3 百度云 http://pan.baidu.com/s/1i5v8geT 密码:1hip

以上链接来自伟大的网络资源!!!!!!!!

接下来参照下面这个链接继续操作:
http://bbs.feng.com/read-htm-tid-10510823-page-1.html
终于又再次进入“macOS 实用工具(见下图:)
这里写图片描述
选择“重新安装macOS” ,点击继续,然后根据屏幕提示继续操作,最后会进入到这样一个界面:
这里写图片描述
当安装完后,电脑会立刻重启,此时要立刻拔了U盘,否则等下会再次从U盘进入。
如果电脑重启后发现还是出现一个问号,该如何是好?(此时是不是已经绝望了!!!!)
不要灰心,我们再次插上U盘试一次,此时我们要用磁盘工具检查一下启动磁盘(通常名为“Macintosh HD”),会发现该盘并没有装载(此时左侧显示出来该盘是灰色的),点击最上面一栏的“装载”,完成后发现该盘的框架会变成黑色。
此时再次进入实用工具,选择“重新安装macOS” ,点击继续,然后根据屏幕提示继续操作。同上一致,安装好后重启要立刻拔掉U盘。
最后,我们就可以再次进入系统了,和原来一模一样,文件没有任何损失。

用meteor快速实现跨平台应用 - CSDN博客

mikel阅读(1191)

来源: 用meteor快速实现跨平台应用 – CSDN博客

作者:华清远见讲师

一:跨平台APP开发

目前采用html5+JavaScript+css 开发跨平台应用正成为一个热点。html5开发网页的骨架,CSS开发网页皮肤,JS用于网页互动。即仅需维护一套代码,通过不同编译方式,适配PC端,ios移动端,Android移动端。 在跨平台框架中,meteor 以快速开发著称。

二: Meteor 强大之处

1. 极速构建

a. 几条命令完成创建

b. 服务端客户端一体(屏蔽通讯实现)

c. 包含数据库

2.Plugin插件 丰富

3.包容第三方(如react)

4.官方文档完善

三:Meteor App 效果演示

meteor官网上有app 效果演示,如下面的截图。功能强大。

四:用meteor 快速实现 App

1. 创建

#curl https://install.meteor.com | /bin/sh 安装meteor

#meteor create a1_hello

#cd a1_hello

#meteor 运行

更改a1_hello.html 保存 网页会同步变化

浏览器访问http://localhost:3000会看到:

//—-源码a1_hello.html 实现界面的显示

Welcome to Meteor!

{{> hello}} //调用模板hello

//定义模板hello (模块对外的显示)

Youve pressed the button {{counter}} times.

//{{counter} 调用模板hello里的对象counter//—-源码a1_hello.js 实现界面的互动

if (Meteor.isClient) { //检测是否是客户端,是则使用下面的代码运行

// counter starts at 0

Session.setDefault(‘counter’, 0); //设置counter的键值为0 — Session 用于处理 键值对(key-value)

Template.hello.helpers({ // 实现模板hello的 helpers方法

counter: function () { // 把对象counter和函数function 关联起来 (接口+实现)

return Session.get(‘counter’); //返回counter的键值

}

});

Template.hello.events({ // 实现模板hello的事件 click button

‘click button’: function () { //click button 是事件,function是事件触发的函数 (接口+实现)

// increment the counter when button is clicked

Session.set(‘counter’, Session.get(‘counter’) + 1); //使counter值加一

}

});

}

if (Meteor.isServer) { //检测是否是服务端,是则使用下面的代码运行

Meteor.startup(function () {

// code to run on server at startup

});

}

2. 列表显示

//—-源码a1_hello.html 加入 列表显示

List

{{> task}}

list 1

list 2

list 3

//—-源码 a1_hello.css 设置 显示效果

body { //设置 html中自带的body标签对象的 显示属性

font-family: sans-serif;

background-color: #315481;

background-image: linear-gradient(to bottom, #315481, #918e82 100%);

background-attachment: fixed;

position: absolute;

top: 0;

bottom: 0;

left: 0;

right: 0;

padding: 0;

margin: 0;

font-size: 14px;

}

.container { //设置自建的类名为container的对象的 显示属性

max-width: 600px;

margin: 0 auto;

min-height: 100%;

background: white;

}

header { //设置header对象的 显示属性

background: #d2edf4;

background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);

padding: 20px 15px 15px 15px;

position: relative;

}

ul { //设置ul列表对象的 显示属性

margin: 0;

padding: 0;

background: white;

}

li { //设置li列表项对象的 显示属性

position: relative;

list-style: none;

padding: 15px;

border-bottom: #eee solid 1px;

}

3. 远程数据库存储

//—-源码a1_hello.html 使用表单输入方式,添加列表内容显示

<head>

<title>list</title>

</head>

<body>

<p class=”container”>

<header>

<h1> List </h1>

<form class=”new-task”>   加入 表单的输入项

<input type=”text” name=”text” placeholder=”Type to add new tasks” />   输入框里灰色的提示信息

</form>

</header>

<ul>

{{#each tasks}}

{{> task}}

{{/each}}

</ul>

</p>

</body>

<template name=”task”>

<li> {{text}}</li>

</template>

//—-源码a1_hello.js 表单输入后能插入数据库中(MongoDB Collection)

Tasks = new Mongo.Collection(“tasks”); //创建数据库连接(MongoDB collection) “tasks” 用于存储列表内容

if (Meteor.isClient) {

// This code only runs on the client

Template.body.helpers({

tasks: function () {

return Tasks.find({},{sort: {createdAt: -1}}); //查找数据库MongoDB内容并排序, 详见API文档 http://docs.meteor.com/#/basic/Mongo-Collection-find

}

});

Template.body.events({

“submit .new-task”: function (event) { //实现对象new-task的submit事件的触发函数

// Prevent default browser form submit

event.preventDefault();

// Get value from form element

var text = event.target.text.value;

// Insert a task into the collection

Tasks.insert({

text: text,

createdAt: new Date() // current time

});

// Clear form

event.target.text.value = “”;

}

});

}

if (Meteor.isServer) {

Meteor.startup(function () {

// code to run on server at startup

});

}

五:丰富组件

搜索组件 https://atmospherejs.com

使用account组件

Ÿ#meteor add accounts-ui

#meteor add accounts-password

六:开源的样例

#meteor create –example todos

#cd todos

#meteor

浏览器访问http://localhost:3000会看到

#meteor create –example localmarket

#cd localmarket

#meteor

浏览器访问http://localhost:3000会看到

文章选自华清远见嵌入式培训

LayUI之table数据表格获取行、行高亮等相关操作

mikel阅读(4015)

来源: ProjectRecord/2、LayUI之table数据表格获取行、行高亮等相关操作.md at master · TangHanF/ProjectRecord

前言

    目前LayUI数据表格既美观有不乏一些实用功能。基本上表格应有的操作已经具备,LayUI作者【贤心】肯定是煞费苦心去优化,此处致敬。但是实话实话,如果单纯那数据表格功能来说,EasUI的数据表格更能更加强大、更加灵活,怎奈于丑的没法说,当然可以美化,但是成本太高了。我相信在后续的LayUI版本更新中,作者应该会着重优化数据表格,因为作为一个前端框架,美观度、效率相关、导航相关、数据展现相关无疑是最重要的。

操作说明

现在转入我们今天要说的数据表格相关操作。目前LayUI数据表格获取行数据的方式有如下方式(个人理解有限,不全之后望提醒):

  1. 表头加入checkbox列,用户选择一行或者多行数据后通过
var checkStatus = table.checkStatus('表格唯一ID值');
var data = checkStatus.data;

获取到相关行数据,其中 data 就是当前选中行的数据对象集合。具体参考: 点击此处直达


但是,如果说没有checkbox,没有行内工具条等设置,就一个常规表格,例如:

目标

  • 要做到双击某一个单元格触发获取整行数据操作,可以拿到当前行tr的DOM对象以及单元格td的DOM对象,以便利用JQuery进行操作,例如高亮显示单元格/行,更改字体颜色等等
  • 能够根据相关条件进行数据筛选、进行高亮显示
  • 能够获取某一个单元格数据,以及该单元格的DOM对象,以便利用JQuery进行操作,例如高亮显示单元格,更改字体颜色等等
  • 能够动态隐藏指定列(实际作用可能不大,因为隐藏数据的话直接在定义cols时不定义即可,LayUI的table数据对象还是会指向你服务端返回的数据,即:你服务端返回哪些字段,table数据容器会原封保留,只是你不在cols定义的话不进行展示,但是数据还是有的),但是有时候可能也需要动态隐藏吧,所以保留了该功能

重点

相关逻辑写在table.render()方法的done回调中,例如:

table.render({
    ....
    ,//详细的配置此处就不描述了
    ,....
    ,done:function(res, curr, count){
        // 此处写逻辑即可
    }
});

相关实现

  • 表格数据 点击此处直达 然后下载或者复制其内容自行新建文件即可。
  • JS实现

新建JavaScript文件,例如新建一个DataTableExtend.js的文件,代码如下:

var LayUIDataTable = (function () {
    var rowData = {};
    var $;

    function checkJquery () {
        if (!$) {
            console.log("未获取jquery对象,请检查是否在调用ConvertDataTable方法之前调用SetJqueryObj进行设置!")
            return false;
        } else return true;
    }

    /**
     * 转换数据表格。
     * @param callback 双击行的回调函数,该回调函数返回三个参数,分别为:当前点击行的索引值、当前点击单元格的值、当前行数据
     * @returns {Array} 返回当前数据表当前页的所有行数据。数据结构:<br/>
     * [
     *      {字段名称1:{value:"当前字段值",cell:"当前字段所在单元格td对象",row:"当前字段所在行tr对象"}}
     *     ,{字段名称2:{value:"",cell:"",row:""}}
     * ]
     * @constructor
     */
    function ConvertDataTable (callback) {
        if (!checkJquery()) return;
        var dataList = [];
        var rowData = {};
        var trArr = $(".layui-table-body.layui-table-main tr");// 行数据
        if (!trArr || trArr.length == 0) {
            console.log("未获取到相关行数据,请检查数据表格是否渲染完毕!");
            return;
        }
        $.each(trArr, function (index, trObj) {
            var currentClickRowIndex;
            var currentClickCellValue;

            $(trObj).dblclick(function (e) {
                var returnData = {};
                var currentClickRow = $(e.currentTarget);
                currentClickRowIndex = currentClickRow.data("index");
                currentClickCellValue = e.target.innerHTML
                $.each(dataList[currentClickRowIndex], function (key, obj) {
                    returnData[key] = obj.value;
                });
                callback(currentClickRowIndex, currentClickCellValue, returnData);
            });
            var tdArrObj = $(trObj).find('td');
            rowData = {};
            //  每行的单元格数据
            $.each(tdArrObj, function (index_1, tdObj) {
                var td_field = $(tdObj).data("field");
                rowData[td_field] = {};
                rowData[td_field]["value"] = $($(tdObj).html()).html();
                rowData[td_field]["cell"] = $(tdObj);
                rowData[td_field]["row"] = $(trObj);

            })
            dataList.push(rowData);
        })
        return dataList;
    }

    return {
        /**
         * 设置JQuery对象,第一步操作。如果你没有在head标签里面引入jquery且未执行该方法的话,ParseDataTable方法、HideField方法会无法执行,出现找不到 $ 的错误。如果你是使用LayUI内置的Jquery,可以
         * var $ = layui.jquery   然后把 $ 传入该方法
         * @param jqueryObj
         * @constructor
         */
        SetJqueryObj: function (jqueryObj) {
            $ = jqueryObj;
        }

        /**
         * 转换数据表格
         */
        , ParseDataTable: ConvertDataTable

        /**
         * 隐藏字段
         * @param fieldName 要隐藏的字段名(field名称)参数可为字符串(隐藏单列)或者数组(隐藏多列)
         * @constructor
         */
        , HideField: function (fieldName) {
            if (!checkJquery()) return;
            if (fieldName instanceof Array) {
                $.each(fieldName, function (index, field) {
                    $("[data-field='" + field + "']").css('display', 'none');
                })
            } else if (typeof fieldName === 'string') {
                $("[data-field='" + fieldName + "']").css('display', 'none');
            } else {

            }
        }
    }
})();
  • 调用

完整示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--<script src="../../js/jquery-3.3.1.js"></script>-->
    <script src="../../js/layui.js"></script>
    <script src="DataTableExtend.js"></script>
    <link rel="stylesheet" href="../../js/css/layui.css" media="all">

    <script>
        (function () {
            layui.use(['table', 'layer'], function () {
                var table = layui.table;
                var layer = layui.layer;
                var $ = layui.jquery;
                table.render({
                    id: "tableID"// 设定表格的唯一ID进行标识
                    , elem: '#tableDataLoad'// 绑定table对应的元素
                    , height: 'full-300'
                    , url: 'data2.json' // TODO: 此处写你实际数据来源
                    , size: 'sm'
                    , page: true
                    , limits: [10, 20, 30, 40, 50]
                    , limit: 30
                    , cols: [[
                        {field: 'match_name', width: 93, align: 'center', title: '比赛名称', rowspan: 2}
                        , {align: 'center', title: '比赛信息', colspan: 3}
                        , {field: 'jingcai', width: 200, align: 'center', title: '竞猜项', rowspan: 2}
                        , {field: 'num', width: 100, align: 'center', title: '竞猜数量', rowspan: 2}
                    ]
                        , [
                            {field: 'match_time_beijing', width: 200, align: 'center', title: '比赛时间'}
                            , {field: 'match_master', width: 100, align: 'center', title: '主队'}
                            , {field: 'match_guest', width: 100, align: 'center', title: '客队'}
                        ]]
                    , done: function (res, curr, count) {// 表格渲染完成之后的回调

                        $(".layui-table th").css("font-weight", "bold");// 设定表格标题字体加粗

                        LayUIDataTable.SetJqueryObj($);// 第一步:设置jQuery对象

                        //LayUIDataTable.HideField('num');// 隐藏列-单列模式
                        //LayUIDataTable.HideField(['num','match_guest']);// 隐藏列-多列模式

                        var currentRowDataList = LayUIDataTable.ParseDataTable(function (index, currentData, rowData) {
                            console.log("当前页数据条数:" + currentRowDataList.length)
                            console.log("当前行索引:" + index);
                            console.log("触发的当前行单元格:" + currentData);
                            console.log("当前行数据:" + JSON.stringify(rowData));

                            var msg = '<div style="text-align: left"> 【当前页数据条数】' + currentRowDataList.length + '<br/>【当前行索引】' + index + '<br/>【触发的当前行单元格】' + currentData + '<br/>【当前行数据】' + JSON.stringify(rowData) + '</div>';
                            layer.msg(msg)
                        })

                        // 对相关数据进行判断处理--此处对【竞猜数量】大于30的进行高亮显示
                        $.each(currentRowDataList, function (index, obj) {
                             /*
                                * 通过遍历表格集合,拿到每行数据对象obj,通过obj["列名"]["row"]可以拿到行对象,obj["列名"]["cell"]可以拿到单元格对象
                                * */
                            if (obj['num'] && obj['num'].value > 30) {
                                obj['num']["row"].css("background-color", "#FAB000");// 对行(row)进行高亮显示
                                obj["num"]["cell"].css("font-weight","bold");// 对单元格(cell)字体进行加粗显示
                            }
                        })
                    }// end done


                });//end table.render

                function dealLighthigh (rowIndexArr, bgColor) {
                    $.each(rowIndexArr, function (index, val) {
                        if (typeof val == "number") {
                            $($(".layui-table-body.layui-table-main tr")[val]).css("background-color", bgColor ? bgColor : "yellow");
                            $($("div .layui-table-fixed.layui-table-fixed-l .layui-table-body tr")[val]).css("background-color", bgColor ? bgColor : "yellow");
                        } else if (typeof val == 'object') {
                            $($(".layui-table-body.layui-table-main tr")[val.rowIndex]).css("background-color", val.bgColor ? val.bgColor : "yellow");
                            $($("div .layui-table-fixed.layui-table-fixed-l .layui-table-body tr")[val.rowIndex]).css("background-color", val.bgColor ? val.bgColor : "yellow");
                        }
                    })
                }


            });// end layui use
        })()
    </script>
</head>
<body>
<table id="tableDataLoad" lay-filter="demo"></table>

</body>
</html>

效果图展示

图一:获取行数据

图二:对符合条件的行进行高亮显示

图三:隐藏列

特别说明

通过 LayUIDataTable.HideField隐藏列之后如果进行排序出现被隐藏的列再次出现的问题,解决方案如下:

监听排序事件,进行隐藏处理

table.on('sort(demo)', function(obj){
    // 此处写你要隐藏的field
    $("[data-field='num']").css('display', 'none');
    //....
    
    
    //.....
    // 其它逻辑自行处理
    //.....
});