bootstrap fileinput 使用記錄


第一次使用bootstrap fileinput碰到了許多坑,做下記錄

需求

  1. 本次使用bootstrap fileinput文件上傳組件,主要用來上傳和預覽圖片。作為一個后台管理功能,為某個表的某個字段,設置1對n的圖片記錄
  2. 網上搜索相關文章大多是一個簡單的上傳功能,對圖片文件預覽顯示,前后端交互並沒多少詳細描述

實現功能

  1. 后台界面例子


2. 在新增和編輯里,需要添加圖片上傳顯示需求,在這里我設置的字段名以_img結尾的圖片都會在編輯新增里顯示bootstrap fileinput組件


3. 點擊選擇,選擇文件后會變成一下情況

多出個上傳按鈕,圖片也會多幾個按鈕,我選擇了刪除和放大圖片的按鈕,還可以顯示圖片單獨上傳按鈕,這里我把它去掉了,統一在下方點擊上傳時,全部上傳。這里重點說下,我選擇的異步上傳方式,選擇多個圖片,上傳后台組件采用的是多個圖片輪詢一張一張上傳
4. 點擊放大按鈕

5. 點擊刪除按鈕,會調用刪除方法,點擊上傳按鈕,顯示如下:

6. 若本來就保存了圖片,在點擊修改的時候,我需要回顯圖片,顯示如下

在沒有繼續上傳圖片的時候,它不會顯示上傳按鈕,圖片左下角小圖標會有所變化
7. 點擊保存后

8. 以上為需要實現的具體需求,下面分析代碼

問題和解決方案

前端

  1. 此處只列出關鍵代碼
    html:
<#elseif modifyField["field_name"]?ends_with("_img")?string == "true">
    <div class="form-group">
        <label class="control-label">${modifyField["field_description"]}:</label>
        <!-- 用來作為form表單提交上傳圖片返回id的集合,這里我采用`,`隔開的字符串形式保存 -->
        <input type="text" hidden id="${modifyField["field_name"]}" name="${modifyField["field_name"]}" value=""/>
        <!-- 用來初始化上傳組件 -->
        <input type="file" name="file" id="${modifyField["field_name"]}_uploadfile" multiple="multiple" class="file-loading" />
    </div>
<#else>

js:

/**
 * 銷毀圖片上傳組件
 * @param initUrl
 */
function destroyUploadImg(){
    //這里我用jquery找到我約定的上傳組件,使用_uploadfile結尾,並遍歷銷毀
    $("#dlg input[id$='_uploadfile']").each(function(index,html){
        var upfile = $(html);
        upfile.fileinput("destroy");
    });
}

/**
 * 初始化fileinput組件
 */
var getFileInput = function(){
    //初始化方法,同樣找到約定組件遍歷
    $("#dlg input[id$='_uploadfile']").each(function(index,html){
        var upfile = $(html);

        var imgId = upfile.attr("id").substring(0,upfile.attr("id").lastIndexOf("_"))
        var val = $("#"+imgId).val();
        var preImageList = val.split(",");

        var initialPreview = [];
        var initialPreviewConfig = [];
        for (var i = 0; i < preImageList.length; i++) {
            if(preImageList[i] === "") {
                preImageList.splice(i,1);
                continue
            }
            initialPreview.push("${request.contextPath}/showPic?image_id=" + preImageList[i] + "&count=" + Math.random())

            var previewConfig = new Object();
            previewConfig.url = "${request.contextPath}/deleteImg.do";
            previewConfig.key = preImageList[i];

            var imageId = preImageList[i];
            previewConfig.extra = {"image_id": imageId,"pRow":JSON.stringify(pRow),"currPageId":${curr_page_id},"updateCol":imgId};//上傳的額外參數,會作為post請求參數提交
            initialPreviewConfig.push(previewConfig);
        }
        if(upfile.length > 0) {
            //元素存在時執行的代碼
            upfile.fileinput({
                theme: 'fa',
                allowedFileExtensions: ['jpg', 'png', 'gif'],//接收的文件后綴
                //        maxFileSize:0,
                language: 'zh', //設置語言
                uploadUrl: "${request.contextPath}/uploadImg.do", //上傳的地址
                uploadAsync: true, //默認異步上傳
                uploadExtraData:{"currPageId":${curr_page_id},"pRow":JSON.stringify(pRow),"colName":imgId},//上傳額外的post請求參數
//                showUpload: false, //是否顯示上傳按鈕
                showRemove : false, //顯示移除按鈕
                showPreview : true, //是否顯示預覽
                showCaption: false,//是否顯示標題
                browseClass: "btn btn-primary", //按鈕樣式
                dropZoneEnabled: true,//是否顯示拖拽區域
                maxFileCount: 10, //表示允許同時上傳的最大文件個數
                enctype: 'multipart/form-data',
                validateInitialCount:true,
                overwriteInitial: false,//是否在上傳下一個文件的時候覆蓋前一個
                initialPreviewAsData: true,//實現初始化預覽
                removeFromPreviewOnError:true,//碰到上傳錯誤的文件,不顯示在框內
                layoutTemplates:{
                    actionUpload:''    //設置為空可去掉上傳按鈕
                    //actionDelete:''  //設置為空可去掉刪除按鈕
                },
                initialPreview: initialPreview,//初始化預覽文件的地址集合
                initialPreviewConfig:initialPreviewConfig//初始化預覽文件配置
            }).on('filepreupload', function(event, data, previewId, index) {     //上傳中
                var form = data.form, files = data.files, extra = data.extra,
                        response = data.response, reader = data.reader;
                console.log('文件正在上傳');
            }).on("fileuploaded", function (event, data, previewId, index) {    //一個文件上傳成功
                console.log('文件上傳成功!');
                if(data.response['result'] === "success"){
                    var imageId = data.response['imageId'];
                    preImageList.push(imageId);
                    var preImageStr = preImageList.join(",");
                    $("#"+imgId).val(preImageStr);
                }else{
                    return false;
                }
            }).on('fileerror', function(event, data, msg) {  //一個文件上傳失敗
                console.log('文件上傳失敗!');
            }).on('filesuccessremove', function(event, id) {
                console.log('文件刪除');
            }).on('filedeleteerror', function(event, id) {
                console.log('文件刪除錯誤');
            }).on('filedeleted', function(event, key,data) {
                //刪除返回結果
                $("#"+imgId).val(data.responseJSON["imageIdStr"]);
                $('#dg').datagrid('reload');
            });
        }
    });
};

明白怎么回事,使用起來還是蠻簡單的,就簡單的兩個創建和銷毀方法,注釋寫的也蠻詳細了,除了業務邏輯,組件的必要注釋都在了
2. 重點的地方
- 文件上傳只要填寫上傳地址和額外參數
- 在fileuploaded方法中做上傳完畢的業務邏輯
- 文件刪除只需要在預覽配置里加上刪除的地址和額外參數,新增的不管有沒有上傳的文件,刪除的僅僅是前端
- 在filedeleted方法中做刪除完畢的業務邏輯

后端

  1. 先上代碼段
 /**
 * 顯示圖片
 * @param request
 * @return
 * @throws Exception
 */
@RequestMapping(value = {"/showPic.do", "showPic"})
public ResponseEntity<byte[]> showPic(HttpServletRequest request) throws Exception {
    HashMap<String, Object> hashMap = this.getHashMap(request);
    String imageId = (String) hashMap.get("image_id");
    Map<String, Object> imgConditions = new HashMap<>();
    imgConditions.put("image_id", imageId);
    Map<String, Object> imgInfo = tableService.getRecord(Arrays.asList("image_path", "uuid", "image_type"), TableConstant.TB_IMG, imgConditions);
    String imageType = (String) imgInfo.get("image_type");
    String fullPath = imgInfo.get("image_path") + (String) imgInfo.get("uuid") + "." + imageType;

    FileInputStream inputStream = new FileInputStream(fullPath);
    int available = inputStream.available();
    byte[] data = new byte[available];
    inputStream.read(data);
    inputStream.close();
    HttpHeaders he = new HttpHeaders();
    he.setContentType(MediaType.valueOf("image/" + imageType));
    log.info("imageId:" + imageId + "讀取");
    return new ResponseEntity<>(data, he, HttpStatus.OK);
}


/**
 * 上傳圖片
 * @param file
 * @return
 */
@RequestMapping(value = "/uploadImg.do")
public @ResponseBody
Map<String, Object> upload(HttpServletRequest request,@RequestParam("file") MultipartFile file) {
    HashMap<String, Object> hashMap = this.getHashMap(request);
    String originalFilename = file.getOriginalFilename();
    Map<String, Object> pRowMap = JSON.parseObject(hashMap.get("pRow").toString(), Map.class);
    String colName = (String) hashMap.get("colName");
    Map<String, Object> result = new HashMap<>();
    result.put("result", "fail");
    if (StringUtils.isNotEmpty(originalFilename)) {
        String[] splitFileName = originalFilename.split("\\.");
        String uuid = UUIDUtil.getUUID();
        Map<String, Object> rowMap = new HashMap<>();

        if (!StringUtils.isEmpty(originalFilename)) {
            splitFileName = originalFilename.split("\\.");
            String refPageId = (String) hashMap.get("currPageId");
            Map<String, Object> pageCondition2 = new HashMap<>();
            pageCondition2.put("page_id", refPageId);
            pageCondition2.put("del_flag", 0);
            Map<String, Object> refPageInfo = tableService.getRecord(Arrays.asList("tb_name", "primary_key"), TableConstant.TB_PAGE, pageCondition2);

            String tbName = (String) refPageInfo.get("tb_name");
            String[] tbNameSpl = tbName.split("_");
            String middlePath = tbNameSpl[tbNameSpl.length - 1];
            String filePath = sysconfig.getProperties().get("image.path") +
                    "/" + middlePath +
                    "/" + DateUtil.format2str("yyyyMMdd") +
                    "/";
            rowMap.put("image_type", splitFileName[splitFileName.length - 1]);
            rowMap.put("image_path", filePath);
            rowMap.put("uuid", uuid);
            rowMap.put("image_name", originalFilename);
            rowMap.put("page_id", refPageId);
            Long incr = redisService.incr(sysconfig.getProperties().get("sys.id") + "_primary_key_" + refPageInfo.get("tb_name") + "_" + refPageInfo.get("primary_key"), 1);
            rowMap.put("image_id", incr);
            rowMap.put("col_name", colName);

            String primaryKey = (String) refPageInfo.get("primary_key");
            String pk = String.valueOf(pRowMap.get(primaryKey));
            rowMap.put("ref_value", pk);
            int ret = 0;
            try {

                ret = tableService.addRecord(TableConstant.TB_IMG, rowMap);
            } catch (Exception e) {
                String message = e.getMessage();
                if (message.contains("Duplicate entry")) {
                    log.info("主鍵沖突,redis刷新主鍵");
                    List<Map<String, Object>> recordsBySQL = tableService.getRecordsBySQL(String.format("select %s from %s order by %s desc limit 1", "image_id", TableConstant.TB_IMG, "image_id"));
                    for (Map<String, Object> objectMap : recordsBySQL) {
                        redisService.set(sysconfig.getProperties().get("sys.id") + "_primary_key_" + TableConstant.TB_IMG + "_" + "image_id", String.valueOf(Integer.parseInt(objectMap.get("image_id").toString())));
                    }
                    incr = redisService.incr(sysconfig.getProperties().get("sys.id") + "_primary_key_" + TableConstant.TB_IMG + "_" + "image_id", 1);
                    rowMap.put("image_id", incr);
                    ret = tableService.addRecord(TableConstant.TB_IMG, rowMap);
                }
            }

            if (ret > 0) {
                log.info(String.format("插入圖片記錄imageId:%s成功", incr));
                try {
                    boolean b = FileUtil.uploadFile(file.getBytes(), filePath, uuid + "." + splitFileName[1]);
                    if (b) {
                        log.info(String.format("插入圖片imageId:%s成功", incr));
                        result.put("result", "success");
                        result.put("imageId", incr);
                    } else {
                        log.info(String.format("插入圖片imageId:%s失敗", incr));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                log.info(String.format("插入圖片記錄imageId:%s失敗", incr));
            }
        }

        return result;
    }

    return result;
}


/**
 * 圖片刪除
 */
@RequestMapping(value = "deleteImg.do")
@ResponseBody
public Map<String, Object> deleteImg(HttpServletRequest request) {
    Map<String, Object> data = new HashMap<>();
    HashMap<String, Object> hashMap = this.getHashMap(request);
    String imageId = (String) hashMap.get("image_id");
    String pageId = (String) hashMap.get("currPageId");
    String updateCol = (String) hashMap.get("updateCol");
    Map<String, Object> pRowMap = JSON.parseObject(hashMap.get("pRow").toString(), Map.class);

    List<Map<String, Object>> records = tableService.getRecordsBySQL(String.format("select image_id,image_path,uuid,image_type from t_uls_image where image_id=%s", imageId));
    String imageIdStr = "";
    for (Map<String, Object> record : records) {
        String imagePath = (String) record.get("image_path");
        String uuid = (String) record.get("uuid");
        String imageType = (String) record.get("image_type");
        String path = imagePath + "/" + uuid + "." + imageType;
        File file = new File(path);
        if (file.exists()) {
            boolean delete = file.delete();
            if (delete) {
                log.info(String.format("圖片imageId:%s刪除成功", imageId));

                // 查找哪張表
                Map<String, Object> tableCondition = new HashMap<>();
                tableCondition.put("page_id", pageId);
                Map<String, Object> pageInfo = tableService.getRecord(Arrays.asList("tb_name", "primary_key"), TableConstant.TB_PAGE, tableCondition);

                String tbName = (String) pageInfo.get("tb_name");
                String primaryKey = (String) pageInfo.get("primary_key");
                String pk = String.valueOf(pRowMap.get(primaryKey));
                List<Map<String, Object>> recordsBySQL = tableService.getRecordsBySQL(String.format("select %s from %s where %s='%s'", updateCol, tbName, primaryKey, pk));
                for (Map<String, Object> objectMap : recordsBySQL) {
                    String o = String.valueOf(objectMap.get(updateCol));
                    String[] split = o.split(",");
                    List<String> strings = new ArrayList<>(Arrays.asList(split));
                    if(strings.contains(imageId)){
                        strings.remove(imageId);
                    }
                    imageIdStr = StringUtils.join(strings, ",");
                }
                tableService.updateRecordsBySQL(String.format("update %s set %s='%s' where %s='%s'", tbName, updateCol, imageIdStr, primaryKey, pk));
            } else {
                log.info(String.format("圖片imageId:%s刪除失敗", imageId));
            }
        }
    }
    data.put("data", "success");
    data.put("imageIdStr", imageIdStr);
    return data;
}
  1. 后端和代碼架構相關了,大致參考下,遇到的問題有
    • 已上傳的圖片有多張的情況,顯示不正確,也就是同時請求后端顯示圖片的時候可能報各種異常,包括不知道哪來的NULLPointException
    • 之后發現,還是代碼架構的問題,繼承類把HttpServletRequest request封裝成了全局變量,並從中取出了hashmap參數封裝。導致在並發的時候,hashmap被覆蓋
  2. 后端主要為框架設計的邏輯,和業務結合,根據實際情況編寫,主要三大塊,上傳,讀取,刪除,問題不大

注意事項

  1. 上傳和刪除操作,后端返回的一定要是json數據,否則會解析錯誤,就算后台上傳成功,前台也顯示失敗

參考API http://plugins.krajee.com/file-input

補充

  1. 后續需要實現上傳圖片后沒提交表單,立即刪除。組件只提供了刪除回調函數。但我需要將上傳文件綁定刪除地址以便調用后台刪除方法
  2. 參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM