WebUploader 同一页面中集成 多实例,单图上传,多图上传,编辑回显,删除(包括逻辑删除,显示移除)

本文详细介绍如何在单页面中实现WebUploader的多实例,分别用于身份证上传、封面图片及附件上传,涵盖代码实现、编辑及回显功能,并提供GitHub上的项目链接。

介于大家的要求我把demo放到了github上,地址:https://github.com/lishuqi/WebUploaderDemo,

这个Demo是基于java的,框架使用Spring+SpringMVC+MyBatis+Maven实现的,数据库使用mysql(只有一张表)

fork的时候顺带star噢~~~

百度 WebUploader

1.最近项目中需要使用 WebUploader来做图片上传,主要功能:

 同一页面中:

添加

a:实现身份证上传,(限制webuploader只能上传一个图片,删除)

b:为微信端和网页端提供封面图片,(限制webuploader只能上传一个图片,删除)

c:附件上传(可以实现多图上传,删除)

效果图:

编辑功能点:

a:图片的回显

b:删除上传

查看功能点:

a:回显

b:不能进行删除,上传等操作

代码实现

前台组件html:

                                          <div class="uploder-container">                                                      
                                    <div  class="cxuploder">
                                        <div class="queueList">
                                            <div class="placeholder">
                                                <div class="filePicker"></div>
                                                <p>将照片拖到这里</p>
                                            </div>
                                        </div>
                                        
                                        <div class="statusBar" style="display:none;height:50px;" >
                                            <div class="btns">
                                                <div  class="jxfilePicker"></div>    
                                            </div>
                                            <div class="info"></div>
                                        </div>
                                    </div>
                                </div>


首先是多实例:

       思路,在一个页面中实现多实例,那么页面会有多个div等组成的webuploader组件,他们的div格式等都是相同的。那么就可以循环初始化这些组件,核心代码如下:

//循环页面中每个上传域   
        $('.uploder-container').each(function(index){//index为标识是页面中第几个webuploader组件
            console.log(index);
            // 添加的文件数量
            var fileCount = 0;
            // 添加的文件总大小
            var fileSize = 0;
            var filePicker=$(this).find('.filePicker');//上传按钮实例
            var queueList=$(this).find('.queueList');//拖拽容器实例
            var jxfilePicker=$(this).find('.jxfilePicker');//继续添加按钮实例
            var placeholder=$(this).find('.placeholder');//按钮与虚线框实例
            var statusBar=$(this).find('.statusBar');//再次添加按钮容器实例
            var info =statusBar.find('.info');//提示信息容器实例
            
            // 图片容器           
            var queue = $('<ul class="filelist"></ul>').appendTo( queueList);
            //一次性可以上传多少张图  
                var i =0;
            if(index == 2){ //附件上传时可以上传50张最多
                 i = 50;
            }else{
                i=1;//身份证,封面图片只能上传一张
            }
            
            //初始化上传实例    
            uploader[index] = WebUploader.create({ 
                pick: {
                    id: filePicker,
                    label: '上传'
                },
                dnd: queueList,
               
                //这里可以根据 index 或者其他,使用变量形式
                accept: {
                    title: 'Images',
                    extensions: 'gif,jpg,jpeg,bmp,png,doc',
                    mimeTypes: 'image/*'
                },
                // swf文件路径
                swf: 'static/res/Uploader.swf',   //ie下很重要
 
               disableGlobalDnd: true,//禁用浏览器的拖拽功能,否则图片会被浏览器打开
 
                chunked: false,//是否分片处理大文件的上传
                            
                server: 'uploadEnsour.do',//上传地址    //后台接受地址
                
                fileNumLimit: i,//一次最多上传文件个数
                
                fileSizeLimit: 10 * 1024 * 1024,    // 总共的最大限制10M
                
                fileSingleSizeLimit: 3 * 1024 * 1024 ,   // 单文件的最大限制3M
                
                auto :true,
                
                formData: {                
                    token:index//可以在这里附加控件编号,从而区分是哪个控件上传的   这个是webuploader组件的index,可以在后台获取到token值,也可以添加其他值以供后台获取
                }
            });
            
            if(index == 2){  //index==1和0时,我这里是封面图片和身份证正面照片,所以就不让他显示。index == 2 时是附件上传所以将其显示可以进行继续添加
                // 添加“添加文件”的按钮
                uploader[index].addButton({
                    id: jxfilePicker,
                    label: '继续添加'
                });
            }
            
            
            //当文件加入队列时触发    uploader[0].upload();
            uploader[index].onFileQueued = function( file ) {
                
                
                fileCount++;
                fileSize += file.size;
 
                if ( fileCount === 1 ) {
                    placeholder.addClass( 'element-invisible' );
                    statusBar.show();
                }
                
                addFile( file,uploader[index],queue);
                setState( 'ready' ,uploader[index],placeholder,queue,statusBar,jxfilePicker);
                updateStatus('ready',info,fileCount,fileSize);
            };
            //当文件被移除队列后触发。
            uploader[index].onFileDequeued = function( file ) {
                fileCount--;
                fileSize -= file.size;
 
                if ( !fileCount ) {
                    setState( 'pedding',uploader[index],placeholder,queue,statusBar,jxfilePicker);
                    updateStatus('pedding',info,fileCount,fileSize);
                }              
                removeFile( file );
            };
            //上传成功(注意:后台返回值这里可以获取到)
           uploader[index].on('uploadSuccess',function(file,reponse){
                
                layer.alert("上传成功",{offset: '250px',icon:1});
            });
            
 
            //可以在这里附加额外上传数据
            
            uploader[index].on('uploadBeforeSend',function(object,data,header) {
                /*var tt=$("input[name='id']").val();
                data=$.extend(data,{
                    modelid:tt
                    });*/
            });
            
        });
        
        
        
        // 当有文件添加进来时执行,负责view的创建
        function addFile( file,now_uploader,queue) {
            var $li = $( '<li id="' + file.id + '">' +
                    '<p class="title">' + file.name + '</p>' +
                    '<p class="imgWrap"></p>'+
                    '<p class="progress"><span></span></p>' +
                    '</li>' ),
                    $btns = $('<div class="file-panel">' +
                            '<span class="cancel">删除</span>' +
                            '<span class="rotateRight">向右旋转</span>' +
                            '<span class="rotateLeft">向左旋转</span></div>').appendTo( $li ),
                        $prgress = $li.find('p.progress span'),
                        $wrap = $li.find( 'p.imgWrap' ),
                        $info = $('<p class="error"></p>');
                
            $wrap.text( '预览中' );
            now_uploader.makeThumb( file, function( error, src ) {
                if ( error ) {
                    $wrap.text( '不能预览' );
                    return;
                }
 
                var img = $('<img src="'+src+'">');
                $wrap.empty().append( img );
            }, thumbnailWidth, thumbnailHeight );
 
            percentages[ file.id ] = [ file.size, 0 ];
            file.rotation = 0;
           
           /*
            file.on('statuschange', function( cur, prev ) {
                if ( prev === 'progress' ) {
                    $prgress.hide().width(0);
                } else if ( prev === 'queued' ) {
                    $li.off( 'mouseenter mouseleave' );
                    $btns.remove();
                }
                // 成功
                if ( cur === 'error' || cur === 'invalid' ) {
                    console.log( file.statusText );
                    showError( file.statusText );
                    percentages[ file.id ][ 1 ] = 1;
                } else if ( cur === 'interrupt' ) {
                    showError( 'interrupt' );
                } else if ( cur === 'queued' ) {
                    percentages[ file.id ][ 1 ] = 0;
                } else if ( cur === 'progress' ) {
                    $info.remove();
                    $prgress.css('display', 'block');
                } else if ( cur === 'complete' ) {
                    $li.append( '<span class="success"></span>' );
                }
                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
            });
            */
            $li.on( 'mouseenter', function() {
                $btns.stop().animate({height: 30});
            });
            $li.on( 'mouseleave', function() {
                $btns.stop().animate({height: 0});
            });
            
 
            $btns.on( 'click', 'span', function() {
                var index = $(this).index(),
                    deg;
 
                switch ( index ) {
                    case 0:
                        now_uploader.removeFile( file );
                        return;
 
                    case 1:
                        file.rotation += 90;
                        break;
 
                    case 2:
                        file.rotation -= 90;
                        break;
                }
 
                if ( supportTransition ) {
                    deg = 'rotate(' + file.rotation + 'deg)';
                    $wrap.css({
                        '-webkit-transform': deg,
                        '-mos-transform': deg,
                        '-o-transform': deg,
                        'transform': deg
                    });
                } else {
                    $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');
                    
                }
 
 
            });
 
            $li.appendTo(queue);
        }
        
        
        // 负责view的销毁
        function removeFile( file ) {
             //view的显示删除
           var $li = $('#'+file.id);
            delete percentages[ file.id ];
           
            $li.off().find('.file-panel').off().end().remove();
            //后台删除文件
            $.ajax({
                url:'deleteFile.do?id='+ file.id,
                type:'GET',
                async:false,
                success:function(result){
                    var t = eval('('+result+')');
                    if(t.status == 99){
                        layer.alert("移除成功!",{icon:1,offset:"200px"});
                    }else{
                        layer.alert("移除失败!",{icon:2,offset:"200px"});
                    }
                }
            });
        } 


回显核心代码

//当文件加入队列时触发    uploader[0].upload();
            uploader[index]    .onFileQueued = function( file ) {
                fileCount++;
                fileSize += file.size;
 
                if ( fileCount === 1 ) {
                    placeholder.addClass( 'element-invisible' );
                    statusBar.show();
                }
                addFile( file,uploader[index],queue);
                setState( 'ready' ,uploader[index],placeholder,queue,statusBar,jxfilePicker);
                updateStatus('ready',info,fileCount,fileSize);
            };
            //加载的时候,页面加载的时候模拟文件加入队列进行图片的编辑回显
            uploader[index]    .on('ready',function(){
                var id = $("#id").val();
                if(typeof(id) == 'undefined'){
                    return;
                }
                $.ajax({
                    url:'getFiles.do?permissionId=' + id + '&index=' + index,//数据库获取文件信息
                    type:'GET',
                    async:false,
                    success:function(files){
                        var files = eval('('+files+')');
                        for(var i = 0; i < files.length; i++){
                            var obj ={};
                            statusMap = {};
                            fileCount++;
                            fileSize += files[i].size;
                            if ( fileCount === 1 ) {
                                placeholder.addClass( 'element-invisible' );
                                statusBar.show();
                            }
                            obj.id=files[i].id;
                            obj.name=files[i].name;
                            obj.type=files[i].type;
                            obj.size=files[i].size;
                            obj.ret=files[i].url;
                            obj.source=this;
                            obj.flog=true;
                            obj.status = 'complete',
                            obj.getStatus = function(){
                                return '';
                            }
                            obj.version = WebUploader.Base.version;
                            obj.statusText = '';
                            obj.setStatus = function(){
                                var prevStatus = statusMap[this.id];
                                typeof text !== 'undefined' && (this.statusText = text);
                                if(status !== prevStatus){
                                    statusMap[this.id] = status;
                                    //文件状态设置为已完成
                                    uploader[index].trigger('statuschage',status,prevStatus);
                                }
                            }
                            addFile( obj,uploader[index],queue);
                            setState( 'ready' ,uploader[index],placeholder,queue,statusBar,jxfilePicker);
                            updateStatus('ready',info,fileCount,fileSize);
                        }
                    }
                });
            });

<think>我们正在讨论的是Bootstrap FileInput文件上传编辑功能实现。根据引用[1]和引用[2],我们可以总结出以下关键点: 1. 前端使用Bootstrap FileInput组件实现文件上传包括选择文件、显示文件列表、上传等功能。 2. 编辑功能包括上传文件的回显(再次编辑显示之前上传的文件)和新增文件同时显示。 3. 后端需要提供文件上传接口,并且前端在上传后需要接收文件信息(如文件名、URL、缩略等)用于显示。 根据用户上一次的问题(bootstrap的fileinput文件上传 springboot后端编辑实例)以及引用内容,我们将分为以下几个部分实现: 一、前端实现(使用Bootstrap FileInput) 二、后端实现(Spring Boot) 三、编辑回显与再次新增 一、前端实现(使用Bootstrap FileInput) 1. 引入必要的CSS和JS文件。 2. 初始化FileInput组件,配置上传地址、允许的文件类型、最大文件数量、是否允许文件等。 3. 配置预览,包括初始预览(initialPreview)和初始预览配置(initialPreviewConfig)用于回显上传的文件。 4. 配置上传成功后的回调,处理服务器返回的文件信息。 二、后端实现(Spring Boot) 1. 文件上传接口:接收MultipartFile数组,保存文件,并返回文件信息(包括文件名、URL、大小等,参考引用[2]中的FileVO类)。 2. 文件删除接口(可选,用于删除上传的文件)。 3. 文件下载接口(用于回显时获取文件,或提供下载)。 三、编辑回显与再次新增 1. 在编辑页面,从后端获取已上传的文件列表,并初始化FileInput组件的initialPreview和initialPreviewConfig。 2. 当用户再次新增文件时,FileInput组件会自动将新文件添加到文件列表中,并显示出来。 下面我们分别展开: 一、前端实现(详细步骤) 1. HTML结构: ```html <input id="file-input" type="file" name="files" multiple> ``` 2. JavaScript初始化: ```javascript $(document).ready(function() { // 初始化FileInput $("#file-input").fileinput({ language: 'zh', // 语言 uploadUrl: '/api/files/upload', // 上传地址 allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'pdf'], // 允许的文件类型 maxFileCount: 10, // 最大文件数量 showUpload: true, // 显示上传按钮 showRemove: true, // 显示移除按钮 showCaption: false, // 是否显示标题 dropZoneEnabled: true, // 是否显示拖拽区域 // 初始预览配置(用于回显) initialPreview: [ // 这里放置预览内容,可以是片的<img>标签,也可以是其他文件的<a>标签等 // 例如:'<img src="/api/files/download/1.jpg" class="file-preview-image">' ], initialPreviewConfig: [ // 每个预览项的配置 // 例如:{caption: "1.jpg", size: 12345, url: '/api/files/delete', key: 1} ], initialPreviewAsData: true, // 设置为true,则initialPreview中的内容将被解析为数据(如URL)而不是直接显示HTML overwriteInitial: false, // 是否覆盖初始预览,设置为false则可以在初始预览基础上添加新文件 // 其他配置... }).on('fileuploaded', function(event, data) { // 文件上传成功后的回调 var response = data.response; if (response.success) { // 可以在这里将上传成功的文件信息添加到某个隐藏域中,或者更新已上传文件列表 console.log('文件上传成功', response.files); } }).on('fileremoved', function(event, id) { // 移除文件时的回调(id为文件在预览中的索引,或者配置中的key值) // 可以在这里发送请求删除服务器上的文件 console.log('文件被移除', id); }); }); ``` 二、后端实现(Spring Boot) 1. 文件上传接口: ```java @RestController @RequestMapping("/api/files") public class FileController { @Value("${file.upload-dir}") private String uploadDir; @PostMapping("/upload") public ResponseEntity<?> uploadFiles(@RequestParam("files") MultipartFile[] files) { List<FileVO> fileList = new ArrayList<>(); for (MultipartFile file : files) { // 保存文件 String filename = saveFile(file); // 构建FileVO对象(参考引用[2]) FileVO fileVO = new FileVO(); fileVO.setName(file.getOriginalFilename()); fileVO.setUrl("/api/files/download/" + filename); // 下载URL fileVO.setSize(formatFileSize(file.getSize())); // 如果是片,可以生成缩略URL,否则使用默认标 if (isImage(file)) { fileVO.setThumbnailPic("/api/files/thumbnail/" + filename); } else { fileVO.setThumbnailPic("/path/to/default/thumbnail"); } fileList.add(fileVO); } // 返回结果 Map<String, Object> result = new HashMap<>(); result.put("success", true); result.put("files", fileList); return ResponseEntity.ok(result); } private String saveFile(MultipartFile file) { // 保存文件到uploadDir,返回保存的文件名(可以使用UUID避免重复) // 省略具体实现 } private boolean isImage(MultipartFile file) { // 判断文件是否为片 String contentType = file.getContentType(); return contentType != null && contentType.startsWith("image"); } } ``` 2. 文件下载接口: ```java @GetMapping("/download/{filename}") public ResponseEntity<Resource> downloadFile(@PathVariable String filename) throws IOException { Path filePath = Paths.get(uploadDir).resolve(filename).normalize(); Resource resource = new UrlResource(filePath.toUri()); if (resource.exists()) { return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") .body(resource); } else { throw new FileNotFoundException("文件未找到: " + filename); } } ``` 3. 文件删除接口(可选): ```java @DeleteMapping("/delete/{filename}") public ResponseEntity<?> deleteFile(@PathVariable String filename) { // 删除文件 Path filePath = Paths.get(uploadDir).resolve(filename).normalize(); try { Files.delete(filePath); return ResponseEntity.ok().body(Map.of("success", true)); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(Map.of("success", false, "error", e.getMessage())); } } ``` 三、编辑回显与再次新增 在编辑页面,我们需要从后端获取当前记录已上传的文件列表,然后将其设置到FileInput的初始预览中。 1. 后端提供获取文件列表的接口(根据业务ID): ```java @GetMapping("/list") public ResponseEntity<List<FileVO>> getFiles(@RequestParam("businessId") Long businessId) { // 根据业务ID查询文件列表 List<FileVO> fileList = fileService.findByBusinessId(businessId); return ResponseEntity.ok(fileList); } ``` 2. 前端在初始化FileInput组件之前,先通过AJAX获取已上传的文件列表,然后设置initialPreview和initialPreviewConfig: ```javascript // 假设我们已经获取了文件列表数据,存储在fileList变量中 var initialPreview = []; var initialPreviewConfig = []; fileList.forEach(function(file) { // 如果是片,使用img标签 if (file.type.startsWith('image')) { initialPreview.push('<img src="' + file.thumbnailPic + '" class="file-preview-image">'); } else { // 其他文件类型,显示一个文件标和文件名 initialPreview.push('<div class="file-preview-other"><i class="fa fa-file"></i> ' + file.name + '</div>'); } initialPreviewConfig.push({ caption: file.name, size: file.size, url: '/api/files/delete', // 删除URL key: file.id, // 文件唯一标识,用于删除时传给服务器 extra: {id: file.id} // 额外参数 }); }); // 初始化FileInput时传入这些初始预览数据 $("#file-input").fileinput({ // ...其他配置... initialPreview: initialPreview, initialPreviewConfig: initialPreviewConfig }); ``` 3. 当用户移除一个文件时,我们可能需要删除服务器上的文件(通过配置的url和key): ```javascript .on('fileremoved', function(event, id, index) { // 这里id就是配置中的key,我们可以发送一个删除请求 var config = initialPreviewConfig[index]; $.ajax({ url: config.url, type: 'DELETE', data: {id: config.key}, // 或者从extra中取 success: function(result) { console.log('文件删除成功'); } }); }); ``` 4. 当用户新增文件时,FileInput组件会自动上传到服务器,并在上传成功后触发fileuploaded事件,我们可以将新文件的信息添加到已上传文件列表中。 这样,我们就实现了文件上传编辑回显和再次新增的功能。 注意事项: - 文件删除操作需要谨慎,确保只有拥有权限的用户才能删除。 - 在编辑时,如果用户删除了一个已存在的文件,除了从预览中移除,还需要从服务器删除,因此需要记录每个文件的唯一标识(如id)并发送删除请求。 以上是一个完整的实现方案,根据实际业务需求可能需要进行调整。</think>### Bootstrap FileInput 文件上传编辑功能实现 #### 一、前端实现(Bootstrap FileInput) ##### 1. 基础文件上传组件 ```html <input id="file-input" type="file" name="files[]" multiple> ``` ##### 2. 初始化配置(带编辑回显) ```javascript // 从后端获取已上传文件数据(编辑时使用) const initialFiles = [ { id: 101, name: "document.pdf", url: "/files/download/101", thumbnailPic: "/files/thumb/101", size: "2.5 MB", type: "application/pdf" }, // 更文件数据... ]; $('#file-input').fileinput({ language: 'zh', uploadUrl: '/api/files/upload', deleteUrl: '/api/files/delete', allowedFileExtensions: ['jpg', 'png', 'pdf', 'docx'], maxFileCount: 10, showUpload: true, showRemove: true, showCaption: false, dropZoneEnabled: true, // 编辑回显配置 initialPreview: initialFiles.map(file => file.type.includes('image') ? `<img src="${file.thumbnailPic}" class="file-preview-image">` : `<div class="file-preview-other">${file.name}</div>` ), initialPreviewConfig: initialFiles.map(file => ({ caption: file.name, size: file.size, url: file.url, key: file.id, // 用于后端识别 extra: { id: file.id } // 额外参数 })), initialPreviewAsData: true, overwriteInitial: false, // 保留初始文件同时允许新增 fileActionSettings: { showRemove: true, showUpload: false, showZoom: true } }); ``` ##### 3. 事件处理 ```javascript // 文件上传成功 $('#file-input').on('fileuploaded', function(event, data) { const file = data.response.file; console.log(`上传成功: ${file.originalName} (ID: ${file.id})`); }); // 文件删除成功 $('#file-input').on('filedeleted', function(event, key) { console.log(`删除文件 ID: ${key}`); $.ajax({ url: '/api/files/delete', type: 'POST', data: { id: key } }); }); // 新增文件预览 $('#file-input').on('fileselect', function(event, files) { console.log(`新增 ${files.length} 个文件`); }); ``` #### 二、后端实现(Spring Boot) ##### 1. 文件信息实体类 ```java public class FileVO { private Integer id; // 文件ID private String name; // 原始文件名 private String url; // 访问URL private String size; // 文件大小 private String thumbnailPic;// 缩略URL private String type; // MIME类型 // 构造方法/getters/setters } ``` ##### 2. 文件上传控制器 ```java @PostMapping("/upload") public ResponseEntity<Map<String, Object>> uploadFiles( @RequestParam("files") MultipartFile[] files, @RequestParam(value = "businessId", required = false) Integer businessId) { Map<String, Object> response = new HashMap<>(); List<FileVO> uploadedFiles = new ArrayList<>(); for (MultipartFile file : files) { FileVO fileVO = fileStorageService.storeFile(file, businessId); uploadedFiles.add(fileVO); } response.put("success", true); response.put("files", uploadedFiles); return ResponseEntity.ok(response); } ``` ##### 3. 文件存储服务 ```java @Service public class FileStorageService { @Value("${file.upload-dir}") private String uploadDir; public FileVO storeFile(MultipartFile file, Integer businessId) { // 生成唯一文件名 String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename(); Path targetLocation = Paths.get(uploadDir).resolve(fileName); // 保存文件 Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING); // 生成缩略(如果是片) String thumbnail = generateThumbnail(file, fileName); // 保存到数据库 FileEntity entity = new FileEntity(); entity.setOriginalName(file.getOriginalFilename()); entity.setStoredName(fileName); entity.setSize(file.getSize()); entity.setBusinessId(businessId); entity = fileRepository.save(entity); // 返回VO对象 return new FileVO( entity.getId(), file.getOriginalFilename(), "/api/files/download/" + fileName, formatFileSize(file.getSize()), thumbnail, file.getContentType() ); } private String generateThumbnail(MultipartFile file, String fileName) { if (file.getContentType().startsWith("image")) { // 使用Thumbnailator生成缩略 Path thumbPath = Paths.get(uploadDir, "thumbs", fileName); Thumbnails.of(file.getInputStream()) .size(200, 200) .toFile(thumbPath.toFile()); return "/api/files/thumb/" + fileName; } return null; } } ``` ##### 4. 文件删除接口 ```java @PostMapping("/delete") public ResponseEntity<?> deleteFile(@RequestParam("id") Integer id) { Optional<FileEntity> fileOpt = fileRepository.findById(id); if (fileOpt.isPresent()) { FileEntity file = fileOpt.get(); // 删除物理文件 Path filePath = Paths.get(uploadDir, file.getStoredName()); Files.deleteIfExists(filePath); // 删除缩略 Path thumbPath = Paths.get(uploadDir, "thumbs", file.getStoredName()); Files.deleteIfExists(thumbPath); // 删除数据库记录 fileRepository.delete(file); return ResponseEntity.ok(Map.of("success", true)); } return ResponseEntity.notFound().build(); } ``` #### 三、编辑回显实现 ##### 1. 获取业务关联文件 ```java @GetMapping("/list") public ResponseEntity<List<FileVO>> getFilesByBusiness( @RequestParam("businessId") Integer businessId) { List<FileEntity> entities = fileRepository.findByBusinessId(businessId); List<FileVO> files = entities.stream().map(entity -> new FileVO( entity.getId(), entity.getOriginalName(), "/api/files/download/" + entity.getStoredName(), formatFileSize(entity.getSize()), entity.getContentType().startsWith("image") ? "/api/files/thumb/" + entity.getStoredName() : null, entity.getContentType() ) ).collect(Collectors.toList()); return ResponseEntity.ok(files); } ``` ##### 2. 前端初始化时加载 ```javascript // 编辑页面加载时获取已上传文件 const businessId = 123; // 当前编辑的业务ID $.get(`/api/files/list?businessId=${businessId}`, function(files) { $('#file-input').fileinput('init', { initialPreview: files.map(file => /* 预览HTML */), initialPreviewConfig: files.map(file => /* 配置对象 */) }); }); ``` #### 四、关键功能说明 1. **编辑回显机制**: - 使用 `initialPreview` 和 `initialPreviewConfig` 初始化已存在文件 - `overwriteInitial: false` 允许在已有文件基础上新增 2. **业务关联**: - 通过 `businessId` 关联文件与业务实体 - 编辑时加载该业务的所有关联文件 3. **缩略生成**: - 使用 Thumbnailator 库自动生成片缩略 - 前端根据文件类型显示不同预览样式 4. **完整生命周期管理**: - 文件上传 → 数据库记录 → 编辑回显删除清理 - 物理文件和数据库记录同步管理 #### 五、最佳实践建议 1. **文件存储优化**: ```java // 使用云存储服务 public class S3FileStorage implements FileStorage { public String storeFile(MultipartFile file) { // 上传到AWS S3/Aliyun OSS return s3Client.putObject(bucketName, fileName, file.getInputStream()); } } ``` 2. **前端验证增强**: ```javascript $('#file-input').fileinput({ // ... validateInitialCount: true, maxFileSize: 5120, // 5MB msgSizeTooLarge: '文件大小超过限制' }); ``` 3. **后端安全防护**: ```java // 文件类型白名单验证 private void validateFileType(MultipartFile file) { String[] allowedTypes = {"image/jpeg", "application/pdf"}; if (!Arrays.asList(allowedTypes).contains(file.getContentType())) { throw new InvalidFileTypeException(); } } ``` > **提示**:对于复杂编辑场景,建议使用 `initialPreviewThumbTags` 实现自定义预览区域[^1],结合后端分片上传技术处理大文件[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值