體驗“超級無敵”的文件上傳組件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文件解析…… 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章