体验“超级无敌”的文件上传组件bootstrap fileinput

网页开发最最重要最最基本的就是富文本编辑器和文件上传,开始我迷信百度的ueditor和webupload,结果总是别扭,看来不能迷信BAT啊。富文本用了froala,文件上传早点用bootstrap fileinput那多炫啊。

参考网上的文章,走了不少弯路。

其实就是喜欢https://plugins.krajee.com/file-krajee-explorer-demo

里面的第二个Advanced Usage 这种列表的样式,如下,好漂亮。

当文件上传成功后,可以预览,可以下载(显示下载按钮),简直不要太棒!!

弯路大家就不要再走了,开始我在git上下载的js啊,css啊,引入本地的jquery.js啊,引入本地的bootstrap的css和js啊,都互相不匹配,折腾了好久。

最简单的办法就是将上述demo网页另存下来,所有的js和css基本就有了,然后用chrome浏览器打开调试,找到Resource,找到页面源码,找到里面引入的js和css,原封不动的放到你的页面中去,就像下面这样:

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/prod/all-krajee.min.css?ver=201903112143" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/1d958cec/css/fileinput.css?ver=201909132002" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/1d958cec/themes/explorer/theme.min.css?ver=201908311938" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/fc69cbca/css/dropdown.min.css" rel="stylesheet">
<script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async></script>
<script src="https://buttons.github.io/buttons.js" async></script>
<link rel="stylesheet" type="text/css" href="/static/font-awesome-4.7.0/css/font-awesome.min.css"/>

<script id="dsq-count-scr" src="//krajee.disqus.com/count.js" async></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/purify.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="/static/bootstrap-fileinput/assets/prod/all-krajee.min.js?ver=201903112143"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/sortable.min.js"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/piexif.min.js"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/fileinput.js?ver=201909132002"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/themes/explorer/theme.min.js?ver=201908311938"></script>
<script src="/static/bootstrap-fileinput/assets/fc69cbca/js/dropdown.min.js"></script>

能够存到本地的,就用本地的css和js,这些本地的js和css,一定要用刚才网页另存下来的里面的css和js,或者在chrome调试里Network下将js和css另存下来也可以。

这样就可以上传啊,预览(如下)啊,效果特别好。

前端完整代码:

<!DOCTYPE html>
<!-- KRAJEE EXPLORER THEME (ADVANCED) -->
<title>规范对标</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/prod/all-krajee.min.css?ver=201903112143" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/1d958cec/css/fileinput.css?ver=201909132002" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/1d958cec/themes/explorer/theme.min.css?ver=201908311938" rel="stylesheet">
<link href="/static/bootstrap-fileinput/assets/fc69cbca/css/dropdown.min.css" rel="stylesheet">
<script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async></script>
<script src="https://buttons.github.io/buttons.js" async></script>
<link rel="stylesheet" type="text/css" href="/static/font-awesome-4.7.0/css/font-awesome.min.css"/>

<script id="dsq-count-scr" src="//krajee.disqus.com/count.js" async></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/purify.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="/static/bootstrap-fileinput/assets/prod/all-krajee.min.js?ver=201903112143"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/sortable.min.js"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/plugins/piexif.min.js"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/js/fileinput.js?ver=201909132002"></script>
<script src="/static/bootstrap-fileinput/assets/1d958cec/themes/explorer/theme.min.js?ver=201908311938"></script>
<script src="/static/bootstrap-fileinput/assets/fc69cbca/js/dropdown.min.js"></script>

<!-- ************ -->

<!-- <link href="/static/bootstrap-fileinput/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> -->
<!-- <link href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" rel="stylesheet"> -->
<!-- <link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css"/> -->

<!-- <link href="/static/bootstrap-fileinput/css/fileinput.min.css" rel="stylesheet"> -->
<!-- <link href="/static/bootstrap-fileinput/themes/explorer/theme.css" rel="stylesheet"> -->

<!-- <script src="/static/bootstrap-fileinput/js/jquery-3.1.1.min.js" crossorigin="anonymous"></script> -->
<!-- <script src="/static/bootstrap-fileinput/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script> -->

<!-- <script src="/static/bootstrap-fileinput/js/fileinput.js"></script> -->
<!-- <script src="/static/bootstrap-fileinput/themes/explorer/theme.js"></script> -->
<!-- <script src="/static/bootstrap-fileinput/js/plugins/piexif.js" type="text/javascript"></script> -->
<!-- <script src="/static/bootstrap-fileinput/js/plugins/sortable.js" type="text/javascript"></script> -->
<!-- <script src="/static/bootstrap-fileinput/js/locales/zh.js" type="text/javascript"></script> -->


<div class="file-loading">
    <input id="input-ke-2" name="input-ke-2[]" type="file" multiple data-browse-on-zone-click="true">
    <!-- <input id="input-b1" name="input-b1" type="file" class="file" > -->
</div>
<script>
<!-- must load the font-awesome.css for this example -->

	$("#input-ke-2").fileinput({
    language:'zh',
    theme: "explorer",
    uploadAsync: false,//同步上传
    uploadUrl: "/v1/fileinput/bootstrapfileinput",
    minFileCount: 1,
    maxFileCount: 5,
    maxFileSize: 10000,
    removeFromPreviewOnError: true,
    overwriteInitial: false,
    previewFileIcon: '<i class="fas fa-file"></i>',
    initialPreview: [
      // IMAGE DATA
      // 'https://picsum.photos/800/560?image=1071',
      // IMAGE RAW MARKUP
      // '<img src="https://picsum.photos/800/560?image=1075" class="kv-preview-data file-preview-image">',
      // TEXT DATA
      // "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut mauris ut libero fermentum feugiat eu et dui. Mauris condimentum rhoncus enim, sed semper neque vestibulum id. Nulla semper, turpis ut consequat imperdiet, enim turpis aliquet orci, eget venenatis elit sapien non ante. Aliquam neque ipsum, rhoncus id ipsum et, volutpat tincidunt augue. Maecenas dolor libero, gravida nec est at, commodo tempor massa. Sed id feugiat massa. Pellentesque at est eu ante aliquam viverra ac sed est.",
      // PDF DATA
      // 'http://kartik-v.github.io/bootstrap-fileinput-samples/samples/pdf-sample.pdf',
      // VIDEO DATA
      // "http://kartik-v.github.io/bootstrap-fileinput-samples/samples/small.mp4"
    ],
    initialPreviewAsData: true, // defaults markup  
    initialPreviewConfig: [
      // {caption: "Image 11.jpg", size: 762980, url: "/site/file-delete", downloadUrl: 'https://picsum.photos/800/460?image=11', key: 11},
      // {previewAsData: false, size: 823782, caption: "Image 13.jpg", url: "/site/file-delete", downloadUrl: 'https://picsum.photos/800/460?image=13', key: 13}, 
      // {caption: "Lorem Ipsum.txt", type: "text", size: 1430, url: "/site/file-delete", key: 12}, 
      // {type: "pdf", size: 8000, caption: "PDF Sample.pdf", url: "/site/file-delete", key: 14}, 
      // {type: "video", size: 375000, filetype: "video/mp4", caption: "Krajee Sample.mp4", url: "/site/file-delete", key: 15} 
    ],
    // initialPreviewDownloadUrl:'https://picsum.photos/1920/1080?image={key}', // the key will be dynamically replaced 
    uploadExtraData: {
      img_key: "1000",
      img_keywords: "happy, nature"
    },
    preferIconicPreview: true, // this will force thumbnails to display icons for following file extensions
       previewFileIconSettings: { // configure your icon file extensions
      'doc': '<i class="fas fa-file-word text-primary"></i>',
      'xls': '<i class="fas fa-file-excel text-success"></i>',
      'ppt': '<i class="fas fa-file-powerpoint text-danger"></i>',
      'pdf': '<i class="fas fa-file-pdf text-danger"></i>',
      'zip': '<i class="fas fa-file-archive text-muted"></i>',
      'htm': '<i class="fas fa-file-code text-info"></i>',
      'txt': '<i class="fas fa-file-text text-info"></i>',
      'mov': '<i class="fas fa-file-video text-warning"></i>',
      'mp3': '<i class="fas fa-file-audio text-warning"></i>',
      // note for these file types below no extension determination logic 
      // has been configured (the keys itself will be used as extensions)
      'jpg': '<i class="fas fa-file-image text-danger"></i>', 
      'gif': '<i class="fas fa-file-image text-muted"></i>', 
      'png': '<i class="fas fa-file-image text-primary"></i>'    
    },
    previewFileExtSettings: { // configure the logic for determining icon file extensions
      'doc': function(ext) {
        return ext.match(/(doc|docx)$/i);
      },
      'xls': function(ext) {
        return ext.match(/(xls|xlsx)$/i);
      },
      'ppt': function(ext) {
        return ext.match(/(ppt|pptx)$/i);
      },
      'zip': function(ext) {
        return ext.match(/(zip|rar|tar|gzip|gz|7z)$/i);
      },
      'htm': function(ext) {
        return ext.match(/(htm|html)$/i);
      },
      'txt': function(ext) {
        return ext.match(/(txt|ini|csv|java|php|js|css)$/i);
      },
      'mov': function(ext) {
        return ext.match(/(avi|mpg|mkv|mov|mp4|3gp|webm|wmv)$/i);
      },
      'mp3': function(ext) {
        return ext.match(/(mp3|wav)$/i);
      }
    }
	}).on("filebatchselected", function (event, data) {//选择即上传
    if (data.length == 0) {
      return;
    }
  });

  // 上传成功回调
  $("#input-ke-2").on("filebatchuploadcomplete", function() {
    alert("上传附件成功");
    // setTimeout("closeUpladLayer()",2000)
  });
  // 上传失败回调
  $('#input-ke-2').on('fileerror', function(event, data, msg) {
    alert(data.msg);
    // tokenTimeOut(data);
  });
</script>

</html> 

服务的golang:

type Fileinput struct {
	InitialPreview       []string        `json:"initialPreview"`
	InitialPreviewConfig []PreviewConfig `json:"initialPreviewConfig"`
	// InitialPreviewDownloadUrl []string        `json:"initialPreviewDownloadUrl"`
}

// type Preview struct {
// 	Url  string
// }

type PreviewConfig struct {
	Caption     string `json:"caption"`
	Size        int64  `json:"size"`
	Url         string `json:"url"`
	DownloadUrl string `json:"downloadUrl"`
	Key         string `json:"key"`
}

// @Title post bootstrapfileinput
// @Description post file by BootstrapFileInput
// @Success 200 {object} SUCCESS
// @Failure 400 Invalid page supplied
// @Failure 404 articl not found
// @router /bootstrapfileinput [post]
//独立上传文件模式
func (c *FileinputController) BootstrapFileInput() {
	//获取上传的文件
	_, h, err := c.GetFile("input-ke-2[]")
	if err != nil {
		beego.Error(err)
	}
	fileSuffix := path.Ext(h.Filename)
	// random_name
	newname := strconv.FormatInt(time.Now().UnixNano(), 10) + fileSuffix // + "_" + filename
	year, month, _ := time.Now().Date()
	err = os.MkdirAll("./attachment/legislation/"+strconv.Itoa(year)+month.String()+"/", 0777) //..代表本当前exe文件目录的上级,.表示当前目录,没有.表示盘的根目录
	if err != nil {
		beego.Error(err)
	}
	var filesize int64

	if h != nil {
		//保存附件
		filepath := "./attachment/legislation/" + strconv.Itoa(year) + month.String() + "/" + newname
		Url := "/attachment/legislation/" + strconv.Itoa(year) + month.String() + "/"
		err = c.SaveToFile("input-ke-2[]", filepath) //.Join("attachment", attachment)) //存文件    WaterMark(path)    //给文件加水印
		if err != nil {
			beego.Error(err)
		}
		filesize, _ = FileSize(filepath)
		filesize = filesize / 1000.0

		// 解析excel
		if fileSuffix == ".XLSX" || fileSuffix == ".xlsx" || fileSuffix == ".XLS" || fileSuffix == ".xls" {
			// xlsx, err := excelize.OpenFile(filepath)
			xlsx, err := xlsx.OpenFile(filepath)
			if err != nil {
				beego.Error(err)
				return
			}
			for _, sheet := range xlsx.Sheets {
				for i, row := range sheet.Rows {
					if i != 0 {
						// 这里要判断单元格列数,如果超过单元格使用范围的列数,则出错for j := 2; j < 7; j += 5 {
						j := 1
						standardtitle := row.Cells[j].String()
						if err != nil {
							beego.Error(err)
						}
						//2、根据名称搜索标准版本库,取得名称和版本号
						library, err := models.SearchLiabraryName(standardtitle)
						// beego.Info(library)
						if err != nil {
							beego.Error(err)
						}
						var LibraryNumber string
						if len(library) != 0 { //library != nil这样不行,空数组不是nil
							// beego.Info(library)
							//3、构造struct
							for j, w := range library {
								// beego.Info(w)
								if j == 0 {
									LibraryNumber = w.Category + " " + w.Number + "-" + w.Year //规范有效版本库中的完整编号
								} else {
									LibraryNumber = LibraryNumber + "," + w.Category + " " + w.Number + "-" + w.Year //规范有效版本库中的完整编号
								}
							}
							row.Cells[j+1].Value = LibraryNumber
						}

					}
				}
			}
			err = xlsx.Save(filepath)
			if err != nil {
				beego.Error(err)
			}
			// for index, name := range xlsx.GetSheetMap() {
			// 	fmt.Println(index, name)
			// 	// Get all the rows in the Sheet1.
			// 	rows, err := xlsx.GetRows(name)
			// 	for _, row := range rows {
			// 		for _, colCell := range row {
			// 			fmt.Print(colCell, "\t")
			// 		}
			// 		fmt.Println()
			// 	}
			// }
		}
		var fileinput Fileinput
		fileinput.InitialPreview = []string{Url + newname}
		// fileinput.InitialPreviewDownloadUrl = []string{Url + newname}
		// var config PreviewConfig
		config := make([]PreviewConfig, 1)
		config[0].Caption = newname
		config[0].DownloadUrl = Url + newname
		config[0].Size = filesize
		config[0].Key = Url + newname
		config[0].Url = Url + newname
		fileinput.InitialPreviewConfig = config
		c.Data["json"] = fileinput
		c.ServeJSON()
	} else {
		c.Data["json"] = map[string]interface{}{"state": "ERROR", "link": "", "title": "", "original": ""}
		c.ServeJSON()
	}
}

注意:服务的用前端里的name值来得到上传的文件。

<input id="input-ke-2" name="input-ke-2[]" type="file" multiple data-browse-on-zone-click="true">

服务的的返回值,必须包含InitialPreview和InitialPreviewConfig,结构体如下:

type Fileinput struct {
	InitialPreview       []string        `json:"initialPreview"`
	InitialPreviewConfig []PreviewConfig `json:"initialPreviewConfig"`
	// InitialPreviewDownloadUrl []string        `json:"initialPreviewDownloadUrl"`
}

type PreviewConfig struct {
	Caption     string `json:"caption"`
	Size        int64  `json:"size"`
	Url         string `json:"url"`
	DownloadUrl string `json:"downloadUrl"`
	Key         string `json:"key"`
}

当InitialPreviewConfig中包含了downloadUrl时,前端收到这个json文件,就会自动显示下载按钮了。

返回值如下:

{initialPreview: ["/attachment/legislation/2019November/1572777975188444800.xlsx"],…}
initialPreview: ["/attachment/legislation/2019November/1572777975188444800.xlsx"]
0: "/attachment/legislation/2019November/1572777975188444800.xlsx"
initialPreviewConfig: [{caption: "1572777975188444800.xlsx", size: 11,…}]
0: {caption: "1572777975188444800.xlsx", size: 11,…}
caption: "1572777975188444800.xlsx"
downloadUrl: "/attachment/legislation/2019November/1572777975188444800.xlsx"
key: "/attachment/legislation/2019November/1572777975188444800.xlsx"
size: 11
url: "/attachment/legislation/2019November/1572777975188444800.xlsx"

我这个是为了写一个规范对标的服务,当用户上传excel文件后,服务端收到excel,进行解析,将excel第二列的所有规范名称循环,从数据库中查询出这个规范名称对应的规范号,填入第三列中,完成后提供给用户下载。注意:这个操作要用同步上传模式,不能用异步上传模式,因为要等待服务端处理完成文件,才能显示下载按钮。

下一步提供word文件解析……

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从流域到海域

Temporal Difference - 时序差分学习

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

8910
来自专栏sofu456

响应式网页

安装react-bootstrap(react-bootstrap标签自定义,属性和bootstrap相同)

7510
来自专栏搜云库技术团队

3万字Spring Boot 核心知识,深入剖析,请收藏

在过去两三年的Spring生态圈,最让人兴奋的莫过于Spring Boot框架。或许从命名上就能看出这个框架的设计初衷:快速的启动Spring应用。因而Spri...

7410
来自专栏菲宇

前端插件之Bootstrap Dual Listbox使用教程双向select选择框控件

原文链接:https://www.jb51.net/article/165996.htm

15020
来自专栏开发架构二三事

netty源码分析之三Bootstrap初始化

可以看到childGroup也就是group方法传入的workerGroup是赋值给ServerBootstrap的childGroup属性的。我们进入 sup...

5320
来自专栏从流域到海域

Model-Free Policy Evaluation 无模型策略评估

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

10820
来自专栏二狗的DBA之路

基于binlog的离线分析平台的一些初步实践

http://quarterback.cn/%e9%80%9a%e8%bf%87kafka-nifi%e5%bf%ab%e9%80%9f%e6%9e%84%e5...

9730
来自专栏Java架构沉思录

这份3万字的Spring Boot知识清单,请查收!

在过去两三年的Spring生态圈,最让人兴奋的莫过于Spring Boot框架。或许从命名上就能看出这个框架的设计初衷:快速的启动Spring应用。因而Spri...

9220
来自专栏开发架构二三事

netty源码分析四之客户端接入与数据写出

在上文Bootstrap初始化流程分析中我们知道,在NioServerSocketChannel进行register时,会调用eventLoop.execute...

4300
来自专栏用户5521492的专栏

给你一份Spring Boot核心知识清单

在过去两三年的 Spring 生态圈,最让人兴奋的莫过于 Spring Boot 框架。或许从命名上就能看出这个框架的设计初衷:快速的启动 Spring 应用。...

7920

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励