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

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

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

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

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

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

弯路大家就不要再走了,开始我在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另存下来也可以。

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

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

前端完整代码:

<!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"

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

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

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

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