从springmvc到springboot 文件上传

文件上传就是将客户端文件复制到服务器的过程。只是上传很简单,但是要加上进度条等等条件就稍微有点复杂

@RestController
@RequestMapping("/v1/images")
@Slf4j
public class ImageController {
    //下面两个属性值来自application.properties配置文件
    @Value("${spring.resources.static-locations}")
    private File resourcePath;
    @Value("${straw.resource.host}")
    private String resourceHost;

    //接收表单上传的文件
    @PostMapping
    public R<String> upload(MultipartFile imageFile) throws IOException {

        //按照当前日期创建文件夹
        String path= DateTimeFormatter.ofPattern("yyyy/MM/dd")
                .format(LocalDate.now());
        //path="2020/12/16"
        File folder=new File(resourcePath,path);
        //folder->F:/resource/2020/12/16
        folder.mkdirs();//创建一串文件夹带s的!!!!
        log.debug("上传的文件夹为:{}",folder.getAbsolutePath());
        //按照上传文件的原始文件名,保留扩展名xx.xx.jpg
        //                              012345678
        String fileName=imageFile.getOriginalFilename();
        String ext=fileName.substring(fileName.lastIndexOf("."));
        //使用UUID生成文件名
        String name= UUID.randomUUID().toString()+ext;
        log.debug("生成的文件名:{}",name);
        //F:/resource/2020/12/16/uuid.jpg
        File file=new File(folder,name);
        //向硬盘写入文件
        imageFile.transferTo(file);
        //直接返回路径方便调用测试
        String url=resourceHost+"/"+path+"/"+name;
        log.debug("访问这个文件的路径为:{}",url);
        return R.ok(url);
    }
}

先说说单文件上传,比较啰嗦,想简单,直接看后面多文件件上传

上传分两种情况:同步和异步。

同步:

          <form id="uploadFile" method="post" enctype="multipart/form-data" action="upload/file">,                其中enctype说明数据用二进制提交。

           <input type="file" name="imageFile" value="选择文件" >,                                                                    其中重点是type必须是file.

           <input type="submit" value="上传文件">          

           </form>

写好了前端,控制器怎么接收呢 ?在控制器类中增加一个控制器:

@PostMapper("/upload/file")

public R uploadFile(MultipartFile imageFile )throw Excetion{                  //参数的名称必须和文件域中的name的名字一致。必须要用MultipartFile

       File folder=new File("E:/upload");  folder.mkdirs();                     //1,创建文件夹,其中mkdir和mkdirs有区别,前者根目录,后者可以是E:/abc/abc/abc

        String fileName=imageFile.getOriginalFilename();                                 //2,创建文件名 约定为原始文件名

        File filePath=new file(folder,filename);                                                            //3,构建文件夹、文件名的路径

        imageFile.transferTo(filePath)                                                                         //4、执行上传,i

        return R.ok("上传完成");                                                                                   //5、传输完成后,通过localhost:8080/xxx.jpg就可以访问了。

}

当然上传完成中只是第一步,如果有必要,确认上传成功后,把文件增加的数据库,以备使用。当然,增加的只是索引(地址)。

异步:

使用ajax上传,区别只是在编写JS文件,

          <form id="uploadFile" method="post" enctype="multipart/form-data" action="upload/file">,                其中enctype说明数据用二进制提交。

           <input type="file" name="imageFile" value="选择文件" >,                                                                    其中重点是type必须是file.

           <input type="submit" value="上传文件">          

           </form>

        <script src="bower-components/jequery/dist/jequery.min.js"></scritp>

         <scritp src="js/utils.js"></scriipt>

         <script>

                 $(#uploadFile").submit(function(){

                 let files=document .getElementById("imageFile").files;

                 if(files.lenth>0){

                     let file==files[0];

                       uploaImage(file);//这个方法在下面定义。

                   }else{alert("用户没有选择文件")}   

                     return false;

                  })             

                 function uploadImage(file){

                         let form=new FormData();

                          form.append("imageFile" file)

                          $.ajax({

                                 url: "upload/file",

                                  method: "post",

                                  data: form,

                                 contentType:false,

                                 processData:false,

                                 success:function(r){ if(r.code==OK){}else{} }//这行是回调函数                                       

                                   })

                         }

                      </script>

一般情况下,文件的传输会很占用资源,所以我们一般建一个静态资源服务器,

这个服务器(可以继承共同的父类),服务器的PROPERTIES中

server.port=8899

# 设置当前服务器默认的静态资源路径
# 普通项目都是static文件夹,这个项目设置到了硬盘上
# Linux和Mac系统设置为 file:/home/xxx
spring.resources.static-locations=file:E:/uploadc

在SUMMERNOTE上浏览和上传图片,我们这样写

<div class="form-group">
  <!--富文本编辑器 start-->
  <label for="summernote">问题正文</label>
  <textarea name="content" id="summernote"></textarea>
  <!--富文本编辑器 end-->
</div>
<script src="../plugins/summernote/summernote.min.js"></script>
<script src="../plugins/summernote/summernote-zh-CN.min.js"></script>
<script>
  $(document).ready(function() {
    $('#summernote').summernote({
      height: 300,
      tabsize: 2,
      lang: 'zh-CN',
      placeholder: '请输入问题的详细描述...',
//上面六行是summernote的基本使用,下面的部分是通过summernote上传图片。
      callbacks:{
        //这个方法会在用户使用富文本编辑器选择图片时运行
        //参数files就是用户选中的图片,默认是数值类型对象,
        //onImageUPload:function(file){}是在summernote中选择图片触发的事件

        onImageUpload:function(files){
          //获取用户选中的第一张图片(稻草项目只考虑一张)
          let file=files[0];
          let form=new FormData();
          form.append("imageFile",file);
          $.ajax({
            url:"/upload/image",
            method:"post",
            data:form,
            contentType:false,
            processData:false,
            //不缓存上传的文件
            cache:false,
            success:function(r){
              console.log(r);
              if(r.code==OK){
                //要将上传的文件显示在富文本编辑器中
                //先定义一个img标签,src属性指向上传成功的文件
                let img=new Image();
                img.src=r.message;
                //summernote提供的方法将img标签显示在富文本编辑器中
                $("#summernote").summernote("insertNode",img);
              }else{
                alert(r.message);
              }
            }
          })
        }
      }
    });
  });
</script>
</body>
<script src="../js/utils.js"></script>
<script src="../js/tags_nav.js"></script>
<script src="../js/createQuestion.js"></script>
# 下面是我们在项目服务器的application.PROPERTIES中自己定义的名称,保存上传图片的路径和访问路径
straw.resource.path=file:E:/upload
straw.resource.host=http://localhost:8899
//获得保存文件的路径和访问路径 这样,我们在控制器类中
@Value("${straw.resource.path}")
private File resourcePath;

@Value("${straw.resource.host}")
private String resourceHost;

@PostMapping("/upload/image")
public R uploadImage(MultipartFile imageFile) throws IOException {
    //  按照日期创建文件夹
    //  path:2021/4/19
    String path= DateTimeFormatter.ofPattern("yyyy/MM/dd")
                    .format(LocalDate.now());
    //  folder:E:/upload/2021/4/19
    File folder= new File(resourcePath,path);
    folder.mkdirs();
    log.debug("上传的目标文件夹:{}",folder.getAbsolutePath());

    //确定上传文件的文件名
    //a.b.c.jpg
    //0123456
    //获得文件的原始文件名
    String fileName=imageFile.getOriginalFilename();
    //获得后缀名  .jpg
    String ext=fileName
            .substring(fileName.lastIndexOf("."));
    //随机创建文件名
    //jakhsdfkjhaksjhdfkj.jpg
    String name= UUID.randomUUID().toString()+ext;
    log.debug("上传文件名为:{}",name);
    //创建要上传的文件对象,并执行上传
    File file=new File(folder,name);
    imageFile.transferTo(file);
    log.debug("文件完整路径:{}",file.getAbsolutePath());

    //返回静态资源服务器可以访问这个图片的路径
    //拼接访问的路径
    String url=resourceHost+"/"+path+"/"+name;
    log.debug("Url:{}",url);
    //记住r对象中的message就是我们要访问的路径
    return R.ok(url);
}

多文件上传

前端实现:

<input type="file" id="uploadFile" multiple="multiplt" value="浏览...">

<script type="text/javascript">

function uploadFileToService(){
    var filrarr = document.getElementById("uploadFile").files;

    var formdata = new FormData();//创建一个表单

    for(var i = 0; i < filrarr.length; i++){
        formdata.append("file", filrarr[i]);

    }

      <code class="javascript" data-origin="" 
      $.ajax({

          url:"/test/",

          type:"POST",

          data:formdata ,

          contentType: false,

          processData: false,

          success:function(rst){ if(rst.state){ var url="/"+rst.data; $('<img class="show-img" src="'+url+'">').appendTo("#imgs") } } });
     <code class="javascript" data-origin=""
}

</script>

后端实现:

@RequestMappiing(value="upload",method=RequestMthod.POST)
@ResponseBody
public String uploadFile(@RequestParam("data") MultipartFile[] files, HttpServletRequest request)throws Exception{
 String filePath = request.getServletContext().getRealPath("/")+"upload/";
 File tmpFile = new File(filePath);
 
 //判断是否存在该文件夹,若不存在则创建文件夹
 if(!tmpFile.exists()){
 tmpFile.mkdir();
 }
 
 for(MultipartFile file:files){
 file.transferTo(new File(filePath+file.getOriginalFilename()));
 }
 return "success";
}

下面的内容比较旧,但其中定议上传规则部分可以吸取

 function InsertRow(){    
        TableRowCount++;       
        if(TableRowCount<=4)
        {
            var tabDW=document.getElementById("EditTable");
            var curRow=tabDW.rows[tabDW.rows.length-1];
            var newRow=tabDW.insertRow(curRow.rowIndex);
            var cell=newRow.insertCell(0);
            cell.innerHTML='文件附件: <input type="file" name="file'+(tabDW.rows.length+1)+'" id="file'+(tabDW.rows.length+1)+'"  class="FileUpload"/>&nbsp;&nbsp;&nbsp;&nbsp;文件描述: <input type="text" name="text'+(tabDW.rows.length+1)+'"  />&nbsp;&nbsp;&nbsp;<input type="button" id="del'+(tabDW.rows.length+1)+'" value=" 删  除 " οnclick="javascript:DeleteRow(this)" class="FileUpload"/>';                        
            document.getElementById("btnups").disabled="";
            document.getElementById("HiddenUpLoadFile").value="";
           
            var tab=tabDW.rows[1].cells[0];
            var state=false;
                           
            if(tab.childNodes[tab.childNodes.length-1].type!="button")
            {
                state=true;               
            }
           
            if(state)
            {
                var obj=document.createElement("INPUT");           
                obj.value=" 删  除 ";
                obj.type="button";
                obj.className="FileUpload"; 
                obj.οnclick=function(){DeleteRow(obj);};//(通用)                 
                tabDW.rows[1].cells[0].appendChild(obj);
                //obj.setAttribute("onclick","javascript:alert('f')",0);(非IE)
                //obj.attachEvent("onclick",function(){DeleteRow(obj)});(IE)
               
            }
           
            var inputid='file'+(tabDW.rows.length+1);
            document.getElementById(inputid).click();                 
        }
        else
        {
            window.alert("最多上传五个文件!");
            TableRowCount--;
        }      
    }
   
   
     function DeleteRow(obj){
        TableRowCount--;
        var curRow=obj.parentNode.parentNode;
        var tabDW=document.getElementById("EditTable");
        tabDW.deleteRow(curRow.sectionRowIndex );  
            
        if(TableRowCount==0)
        {
            var cell=tabDW.rows[1].cells[0];
            cell.removeChild(cell.childNodes[cell.childNodes.length-1]);
        }
     }

 

<table cellspacing="0" cellpadding="0"  style="border:1px solid #BBE9FF" id="EditTable">
                              <tr class="altbg1">
                                <td style="border-bottom:1px solid #BBE9FF"><span class="bold">上传新附件</span> ( 小于 512 kb
                                  , 可用扩展名: rar, zip, gif, jpg, png) </td>
                              </tr>
                              <tr class="altbg1">
                                <td> 文件附件:
                                <input type="file" name="file0" id="file0" />
         &nbsp;&nbsp;&nbsp;文件描述:
                                 <input type="text" name="text0" /></td>
                              </tr>
       <tr class="altbg1" >
       <td> <input type="button" value=" 新  增 " οnclick="javascript:InsertRow()"  id="btnTop"/></td>
       </tr> </table> 

 

//后台上传代码

private void UpFile()
        {
            LoadFile loadFile = new LoadFile();
            int i = 0;
            for (; i < Request.Files.Count; i++)
            {
                HttpPostedFile file = Request.Files[i];//关键部分
                if (!loadFile.ValidataFile(file.ContentLength, file.FileName))
                {
                    Error.InnerHtml = "<script language='javascript'>window.alert('您好上传文件超过限定大小!');</script>";
                    return;
                }
            }


            //验证通过后用NewGameBBSData.ReleaseData中InsertReleaseInfo()方法将数据插入数据库
            //上传文件
            string[] savePath = new string[Request.Files.Count];
            string[] fileName = new string[Request.Files.Count];
            string[] fileSize = new string[Request.Files.Count];
            string[] fileExplain = new string[Request.Files.Count];
            int k = 0;
            try
            {
                for (i = 0; i < Request.Files.Count; i++)
                {
                    HttpPostedFile file = Request.Files[i];
                    if (!String.IsNullOrEmpty(file.FileName))
                    {
                        savePath[i] = loadFile.UploadPhoto(file);
                        fileName[i] = loadFile.GetFileName(file.FileName);
                        fileSize[i] = file.ContentLength.ToString();
                        if (i > 0)
                        {
                            k = i + 3;
                        }
                        else
                        {
                            k = i;
                        }
                        fileExplain[i] = Request.Form[string.Format("{0}{1}", "text", k)];
                    }
                }
            }
            catch
            {
            }
            //将主贴文章的附件信息存入数据库中
            NewGameBBSLib.Revert revert = new NewGameBBSLib.Revert();
            for (i = 0; i < Request.Files.Count; i++)
            {
                if (!String.IsNullOrEmpty(savePath[i]))
                {
                    revert.ArticleID = -2;
                    revert.SavePath = savePath[i];
                    revert.FileName = fileName[i];
                    revert.FileSize = fileSize[i];
                    revert.Explain = fileExplain[i];
                    revert.AddArticleFile();
                }
            }
        }

 

Spring Boot + Vue 前后端分离,两种文件上传方式总结

在Vue.js 中,如果网络请求使用 axios ,并且使用了 ElementUI 库,那么一般来说,文件上传有两种不同的实现方案:

  1. 通过 Ajax 实现文件上传
  2. 通过 ElementUI 里边的 Upload 组件实现文件上传

两种方案,各有优缺点,我们分别来看。

准备工作

首先我们需要一点点准备工作,就是在后端提供一个文件上传接口,这是一个普通的 Spring Boot 项目,如下:

SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");
@PostMapping("/import")
public RespBean importData(MultipartFile file, HttpServletRequest req) throws IOException {
    String format = sdf.format(new Date());
    String realPath = req.getServletContext().getRealPath("/upload") + format;
    File folder = new File(realPath);
    if (!folder.exists()) {
        folder.mkdirs();
    }
    String oldName = file.getOriginalFilename();
    String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
    file.transferTo(new File(folder,newName));
    String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/upload" + format + newName;
    System.out.println(url);
    return RespBean.ok("上传成功!");
}

这里的文件上传比较简单,上传的文件按照日期进行归类,使用 UUID 给文件重命名。

这里为了简化代码,我省略掉了异常捕获,上传结果直接返回成功,后端代码大伙可根据自己的实际情况自行修改。

Ajax 上传

在 Vue 中,通过 Ajax 实现文件上传,方案和传统 Ajax 实现文件上传基本上是一致的,唯一不同的是查找元素的方式。

<input type="file" ref="myfile">
<el-button @click="importData" type="success" size="mini" icon="el-icon-upload2">导入数据</el-button>

在这里,首先提供一个文件导入 input 组件,再来一个导入按钮,在导入按钮的事件中来完成导入的逻辑。

importData() {
  let myfile = this.$refs.myfile;
  let files = myfile.files;
  let file = files[0];
  var formData = new FormData();
  formData.append("file", file);
  this.uploadFileRequest("/system/basic/jl/import",formData).then(resp=>{
    if (resp) {
      console.log(resp);
    }
  })
}

关于这段上传核心逻辑,解释如下:

  1. 首先利用 Vue 中的 $refs 查找到存放文件的元素。
  2. type 为 file 的 input 元素内部有一个 files 数组,里边存放了所有选择的 file,由于文件上传时,文件可以多选,因此这里拿到的 files 对象是一个数组。
  3. 从 files 对象中,获取自己要上传的文件,由于这里是单选,所以其实就是数组中的第一项。
  4. 构造一个 FormData ,用来存放上传的数据,FormData 不可以像 Java 中的 StringBuffer 使用链式配置。
  5. 构造好 FromData 后,就可以直接上传数据了,FormData 就是要上传的数据。
  6. 文件上传注意两点,1. 请求方法为 post,2. 设置 Content-Type 为 multipart/form-data 。

这种文件上传方式,实际上就是传统的 Ajax 上传文件,和大家常见的 jQuery 中写法不同的是,这里元素查找的方式不一样(实际上元素查找也可以按照JavaScript 中原本的写法来实现),其他写法一模一样。这种方式是一个通用的方式,和使用哪一种前端框架无关。最后再和大家来看下封装的上传方法:

export const uploadFileRequest = (url, params) => {
  return axios({
    method: 'post',
    url: `${base}${url}`,
    data: params,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

经过这几步的配置后,前端就算上传完成了,可以进行文件上传了。

使用 Upload 组件

如果使用 Upload ,则需要引入 ElementUI,所以一般建议,如果使用了 ElementUI 做 UI 控件的话,则可以考虑使用 Upload 组件来实现文件上传,如果没有使用 ElementUI 的话,则不建议使用 Upload 组件,至于其他的 UI 控件,各自都有自己的文件上传组件,具体使用可以参考各自文档。

<el-upload
  style="display: inline"
  :show-file-list="false"
  :on-success="onSuccess"
  :on-error="onError"
  :before-upload="beforeUpload"
  action="/system/basic/jl/import">
  <el-button size="mini" type="success" :disabled="!enabledUploadBtn" :icon="uploadBtnIcon">{{btnText}}</el-button>
</el-upload>
  1. show-file-list 表示是否展示上传文件列表,默认为true,这里设置为不展示。
  2. before-upload 表示上传之前的回调,可以在该方法中,做一些准备工作,例如展示一个进度条给用户 。
  3. on-success 和 on-error 分别表示上传成功和失败时候的回调,可以在这两个方法中,给用户一个相应的提示,如果有进度条,还需要在这两个方法中关闭进度条。
  4. action 指文件上传地址。
  5. 上传按钮的点击状态和图标都设置为变量 ,在文件上传过程中,修改上传按钮的点击状态为不可点击,同时修改图标为一个正在加载的图标 loading。
  6. 上传的文本也设为变量,默认上传 button 的文本是 数据导入 ,当开始上传后,将找个 button 上的文本修改为 正在导入

相应的回调如下:

onSuccess(response, file, fileList) {
  this.enabledUploadBtn = true;
  this.uploadBtnIcon = 'el-icon-upload2';
  this.btnText = '数据导入';
},
onError(err, file, fileList) {
  this.enabledUploadBtn = true;
  this.uploadBtnIcon = 'el-icon-upload2';
  this.btnText = '数据导入';
},
beforeUpload(file) {
  this.enabledUploadBtn = false;
  this.uploadBtnIcon = 'el-icon-loading';
  this.btnText = '正在导入';
}
  1. 在文件开始上传时,修改上传按钮为不可点击,同时修改上传按钮的图标和文本。
  2. 文件上传成功或者失败时,修改上传按钮的状态为可以点击,同时恢复上传按钮的图标和文本。

上传效果图如下:

总结

两种上传方式各有优缺点:

  1. 第一种方式最大的优势是通用,一招鲜吃遍天,到哪里都能用,但是对于上传过程的监控,进度条的展示等等逻辑都需要自己来实现。
  2. 第二种方式不够通用,因为它是 ElementUI 中的组件,得引入 ElementUI 才能使用,不过这种方式很明显有需多比较方便的回调,可以实现非常方便的处理常见的各种上传问题。
  3. 常规的上传需求第二种方式可以满足,但是如果要对上传的方法进行定制,则还是建议使用第一种上传方案。

 

springboot+vue 前端、后端到数据库

后端:springboot

前端框架:vue

数据库:mysql

pom.xml:

    <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
controller:

@RestController
@RequestMapping("/yfjs")
@CrossOrigin
public class YFJSController {
 
    @Autowired
    private YFJSService yfjsService;
    @Autowired
    private FJSCService fjscService;
    
    private String url;
     
    @RequestMapping(value="/file",produces="application/json;charset=UTF-8")
    @ResponseBody
    public JSONObject uploadFile(@RequestParam("file") MultipartFile file,@RequestParam("yundanhao")String yundanhao,@RequestParam("jiyunxiang_Id")String jiyunxiang_Id) {
        JSONObject rst = new JSONObject();
        rst.put("yundanhao",yundanhao);
        rst.put("id",jiyunxiang_Id);
        System.out.print("上传文件==="+"\n");
        //判断文件是否为空
        if (file.isEmpty()) {
            rst.put("msg","文件为空");
            return rst;
        }
        // 获取文件名
        String fileName = file.getOriginalFilename();
       // System.out.print("上传的文件名为: "+fileName+"\n");
 
        fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "_" + fileName;
        System.out.print("(加个时间戳,尽量避免文件名称重复)保存的文件名为: "+fileName+"\n");
        //加个时间戳,尽量避免文件名称重复
        //String path = "/opt/apache-tomcat-8.0.50/webapps/img/" +fileName;
        //String path = "/opt/apache-tomcat-8.0.50/webapps/img/" +fileName;
        String path = "/opt/tomcat/apache-tomcat-8.0.50/webapps/img/" +fileName;
        //String path = "E:/fileUpload/" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "_" + fileName;
        //文件绝对路径
        System.out.print("保存文件绝对路径"+path+"\n");
        //创建文件路径
        File dest = new File(path);
        //判断文件是否已经存在
        if (dest.exists()) {
            rst.put("msg","文件已经存在");
            return rst;
        }
       
        try {
            //上传文件
            file.transferTo(dest); //保存文件
            System.out.print("保存文件路径"+path+"\n");
            //url="http://你自己的域名/项目名/images/"+fileName;//正式项目
            url="http://测试服务器:tomcat端口号/img/"+fileName;//本地运行项目
            //url="http://正式服务器2:tomcat端口号/img/"+fileName;//本地运行项目
            JSONObject param = new JSONObject();
            param.put("name", fileName);
            param.put("url", url);
            param.put("yundanhao", yundanhao);
            param.put("jiyunxiang_Id", jiyunxiang_Id);
            JSONObject param1 = new JSONObject();
            param1.put("name", fileName);
            param1.put("yundanhao", yundanhao);
            param1.put("jiyunxiang_Id", jiyunxiang_Id);
            System.out.print("插入结果"+yundanhao+jiyunxiang_Id+"\n");
            fjscService.deleteFJSC(param1);
            System.out.print("插入结果"+yundanhao+jiyunxiang_Id+"\n");
            int jieguo= fjscService.insertUrl(param);
            System.out.print("插入结果"+jieguo+"\n");
            System.out.print("保存的完整url===="+url+"\n");
 
        } catch (IOException e) {
            rst.put("msg","上传失败");
            return rst;
        }
        rst.put("msg","上传成功");
        return rst;
    }
}
 service:
 

package com.heeexy.example.service;
 
import com.alibaba.fastjson.JSONObject;
 
public interface FJSCService {
    
    public int insertUrl(JSONObject jsonObject);
    
    
    JSONObject deleteFJSC(JSONObject jsonObject);
 
 
}
serviceImpl:

package com.heeexy.example.service.impl;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.alibaba.fastjson.JSONObject;
import com.heeexy.example.dao.FJSCDao;
import com.heeexy.example.service.FJSCService;
import com.heeexy.example.util.CommonUtil;
@Service
public class FJSCServiceImpl implements FJSCService{
 
    @Autowired
    private FJSCDao fjscDao;
 
    @Override
    public int insertUrl(JSONObject jsonObject) {
        System.out.println(jsonObject.get("yundanhao").toString()+"----------------"+jsonObject.get("jiyunxiang_Id").toString());
        String[] yundan = jsonObject.get("yundanhao").toString().split(",");
        String[] id = jsonObject.get("jiyunxiang_Id").toString().split(",");
        int res = 0;
        for(int i = 0 ; i < id.length;i++){
            System.out.println(id[i]+"----------------"+yundan[i]);
            jsonObject.put("jiyunxiang_Id",id[i]);
            jsonObject.put("yundanhao",yundan[i]);
            res=fjscDao.insertUrl(jsonObject);
        }
        return res;
    }
 
    
 
    @Override
    public JSONObject deleteFJSC(JSONObject jsonObject) {
        String[] yundan = jsonObject.get("yundanhao").toString().split(",");
        String[] id = jsonObject.get("jiyunxiang_Id").toString().split(",");
        String[] name = jsonObject.get("name").toString().split(",");
        int rst = 0 ;
        for(int i = 0 ; i < id.length;i++){
            jsonObject.put("jiyunxiang_Id",id[i]);
            jsonObject.put("yundanhao",yundan[i]);
            jsonObject.put("name",name[i]);
            rst = fjscDao.deleteFJSC(jsonObject);
            if(rst==0){
                jsonObject.put("msg", "删除失败");
                break;
            }
        }
        if(rst>0){
            jsonObject.put("msg", "删除成功");
            
        }
        return jsonObject;
       
    }
    
    public List<String> getList(String id) {
        List<String> list = new ArrayList<String>();
        
        String[] str = id.split(",");
        for (int i = 0; i < str.length; i++) {
            list.add(str[i]);
        }
        return list;
    
    }
 
 
    
    
}
dao:
 

package com.heeexy.example.dao;
 
import java.util.List;
 
import com.alibaba.fastjson.JSONObject;
import com.heeexy.example.bean.FJSC;
 
public interface FJSCDao {
  
 
    public int insertUrl(JSONObject param);
    
    
    int deleteFJSC(JSONObject jsonObject);
 
 
 
 
}
mapper:
 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
        
 <mapper namespace="com.heeexy.example.dao.FJSCDao">
 
    <resultMap type="com.heeexy.example.bean.FJSC" id="FJSCMap">
        <id column="id" property="id"/>
        <result column="url" property="url"/>        
        <result column="name" property="name"/>
        <result column="yundanhao" property="yundanhao"/>
        <result column="jiyunxiang_Id" property="jiyunxiang_Id"/>
        <result column="dr" property="dr"/>
    
    </resultMap>
    
    <insert id="insertUrl" parameterType="com.alibaba.fastjson.JSONObject">
       insert into fjsc (name,url,yundanhao,jiyunxiang_Id) values (#{name},#{url},#{yundanhao},#{jiyunxiang_Id})
    </insert>
 
    
    
    <delete id="deleteFJSC" parameterType="com.alibaba.fastjson.JSONObject">
         delete from fjsc where yundanhao = #{yundanhao} and jiyunxiang_Id = #{jiyunxiang_Id} and name=#{name}
     </delete>
    
</mapper>
vue:

 <el-button type="primary" icon="plus" @click="queren(sels)" :disabled="this.sels.length === 0" v-if="hasPerm('yfjs:querens')">文件上传</el-button>
 
import qs from "qs";
import axios from 'axios';
 
 
 
export default {
  data() {
    return {
     res:"",
      file: '',
      excelfile: '',
      sels: [], //选中的值显示
      tableList: [],
      listXX:[],
      listFJ:[],
      total: 0,
      start: 0,
      size: 10,   
    
      totalCount: 0, //分页组件--数据总条数
      list: [], //表格的数据
      listLoading: false, //数据加载等待动画
      listQuery: {
        pageNum: 1, //页码
        pageRow: 20, //每页条数
        bancihao: "",
        yundan:"",
        biduizhuangtai: "",
        userId1:this.$store.getters.userId,
        rolue:"",
      },
      rolesname: this.$store.getters.role,
      dialogStatu: "queren",
      dialogFormVisibles: false,
      textMaps: {
        queren: "确认"
      },
 
      dialogFormVisible: false,
   
      
      qrxx: {
        yundan:"",
        id: "",
        huming: "",
        kaihuyinhang: "",
        zhanghao: "",
        shuiwudengjihao: "",
        userId: "",
        deleteflag: "",
        yundanhao:"",
        jiyunxiang_id:"",
        yundanhao:""
      },
      editObj:{
                id:'',
            }
 
    };
  },
  
  methods: {
     getFile: function (event) {
    
      this.file = event.target.files[0];
    
    },
    getexcelFile: function (event) {
    
      this.excelfile = event.target.files[0];
    
    },
    excelFilesubmit: function (event) {
     
      if (this.excelfile == null || this.excelfile == '') {
        alert("文件为空,请选择文件进行导入");
        return;
      }
      //阻止元素发生默认的行为
      event.preventDefault();
      let formData = new FormData();
      formData.append("file", this.excelfile);
      var url = this.HOST + "/ysjhqr/upload";
      axios.post(url, formData)
        .then(function (response) {
          alert(response.data);
          console.log(response);
          window.location.reload();
        }).catch(function (error) {
          alert("比对数据失败,请核对excel表格数据");
          console.log(error);
        });
    },
     getfujianList: function (yundanhao,id){
     this.api({
        url: "/yfjs/selectFJSC",
        method: "post",
         params: {
              yundanhao:yundanhao,
              id:id,
            }
      }).then((data) => {
        this.$set(this.listFJ = data.list);
        this.listLoading = false;
        console.log(data);
      }).catch((aa) => { 
        console.log(aa)
      });   
      
    },
    fujianshangchuan: function (event) {
      if (this.file == null || this.file == '') {
        alert("文件为空,请选择文件进行导入");
        return;
      }
      //阻止元素发生默认的行为
      event.preventDefault();
      let formData = new FormData();
      var yundan = this.qrxx.yundan;
      var id = this.qrxx.id;
      formData.append("file", this.file);
      formData.append("yundanhao",yundan);
      formData.append("jiyunxiang_Id",id);
    
      var url = this.HOST + "/yfjs/file";
      this.$axios.post(url, formData)
        .then(data => {
              console.log(data);
              this.getfujianList(data.data.yundanhao,data.data.id);
             
            })
        .catch(function (error) {
          alert("上传失败");
          console.log(error);
          alert(error);
          
        });
        
    },
   
  
    getLists() {
      //查询列表
      if (!this.hasPerm("yfjs:lists")) {
        return;
      }
 
      this.listLoading = true;
      this.listQuery.rolue = this.$store.getters.role
      this.api({
        url: "/yfjs/listYFJSS",
        method: "get",
        params: this.listQuery
      }).then(data => {
        this.listLoading = false;
        this.list = data.list;
        this.totalCount = data.totalCount;
        console.log(data);
      });
     
    },
    handleSizeChange(val) {
      //改变每页数量
      this.listQuery.pageRow = val;
      this.handleFilter();
    },
    handleFilter() {
      //查询事件
      this.listQuery.pageNum = 1;
      this.getLists();
    },
    handleCurrentChange(val) {
      //改变页码
      this.listQuery.pageNum = val;
      this.getLists();
    },
    getIndex($index) {
      // alert($index);
      //表格序号
      return (this.listQuery.pageNum - 1) * this.listQuery.pageRow + $index + 1;
    },
    queren(sels) {
      //显示新增对话框
     
     var ids = [];
      sels.forEach(element => {
        ids.push(element.id);
      });
 
      this.qrxx.id = ids.join(",");
      var yundans = [];
      sels.forEach(element => {
        yundans.push(element.yundanhao);
      });
 
      this.qrxx.yundan = yundans.join(",");
      this.qrxx.huming = "";
      this.qrxx.kaihuyinhang = "";
      this.qrxx.zhanghao = "";
      this.qrxx.shuiwudengjihao = "",
      this.qrxx.userId = this.$store.getters.userId;
      this.qrxx.deleteflag = 0;
      this.dialogStatu = "queren";
      this.dialogFormVisibles = true;
    },
   
    selsChange(sels) {
      this.sels = sels;
    },
 
  
  }
};
</script>
 

数据库:

 

Vue+SpringBoot实现文件上传

 

 

Vue+SpringBoot实现文件上传

前端页面:

 

前端页面

 

  • 可以看到,该页面包含选择文件以及输入姓名两个表单。所以我并没有选择用form表单提交数据,而是直接使用Vue的双向数据绑定获取的文件信息与输入信息。
  • 在提交时,你可能会遇到各种各样的问题,其中包括跨域问题,跨域问题的简单解决方案会在后端代码中给出。

前端代码:

<template>
    <div>
      <div class="con">
        <div class="tip">选择文件:</div>
        <input class="file" type="file" title="请选择文件" value="请选择文件">
        <div class="tip">输入姓名:</div>
        <input class="inputS" type="text" v-model="name" placeholder="请在此输入姓名">
        <button class="submit" @click="submit">提交</button>
      </div>
    </div>
</template>

<script>
    import axios from 'axios'

    var formData = new FormData() // 声明一个FormData对象
    var formData = new window.FormData() // vue 中使用 window.FormData(),否则会报 'FormData isn't definded'
    export default {
      data() {
        return {
          name: '',
          // file: ''
        }
      },
      methods: {
        submit: function() {
          formData.append('file', document.querySelector('input[type=file]').files[0]) // 'file' 这个名字要和后台获取文件的名字一样;
          formData.append('user',this.name)
          //'userfile'是formData这个对象的键名
          axios({
            url: 'https://****:8081/ensure/uploadwork',   //****: 你的ip地址
            data: formData,
            method: 'post',
            headers: {
              'Content-Type': 'multipart/form-data',
              // 'Access-Control-Allow-Origin': 'http://127.0.0.1:8080'
              //这里是为了解决跨域问题,但是博主并没有用这种方式解决。后面会给出解决方案
            }
          }).then((res) => {
            console.log(res.data);
          }) // 发送请求
        },
      }
    }
</script>

<style scoped>
     // css属性由读者自行实现
</style>

后端代码:

@CrossOrigin
@RestController
@RequestMapping("/ensure")
public class MyController {
    //上传文件
    @PostMapping("/uploadwork")
    public String uploadWork(HttpServletRequest request,@RequestParam(value = "file", required = false) MultipartFile file) throws IOException {

        request.setCharacterEncoding("UTF-8");
        String user = request.getParameter("user");

        if(!file.isEmpty()) {
            String fileName = file.getOriginalFilename();
            String path = null;
            String type = fileName.indexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()) : null;
            if (type != null) {
                if ("DOCX".equals(type.toUpperCase())||"DOC".equals(type.toUpperCase())) {
                    // 项目在容器中实际发布运行的根路径
                    String realPath = request.getSession().getServletContext().getRealPath("/");
                    // 自定义的文件名称
                    String trueFileName = user + "_" + fileName;

                    // 设置存放图片文件的路径
                    path = "/workplace/classwork/" + trueFileName;
                    File dest = new File(path);
                    //判断文件父目录是否存在
                    if (!dest.getParentFile().exists()) {
                        dest.getParentFile().mkdir();
                    }

                    file.transferTo(dest);

                    return trueFileName;
                }else {
                    return "error";
                }
            }else {
                return "error";
            }
        }else {
            return "error";
        }
    }
}
  • 这段代码看起来可能会有一点难理解,但相信你只需要稍加思索就可以理解啦。
  • **@CrossOrigin: **为Spring Boot的实现类加上此注解即可轻松解决跨域问题,这是不是要比上面在前端解决跨域问题简单得多呢。

提交成功:

 

 

 

 

上传档案

使用技术?

springboot+vue完成最基本的文件上传
1、vue
在上传的点击按钮中把input标签的type设置为file,使它变成一个文件组件
文件上传和文件储存(springboot+vue)_第1张图片
特点:文件组件没有数据绑定,否则页面会报错
文件上传和文件储存(springboot+vue)_第2张图片

vue中把文件传到后端去?

怎么触发这个动作(传到后端的这个动作)
文件上传和文件储存(springboot+vue)_第3张图片
方法一
增加按钮
方法二
直接在这个控件上增加一个事件监听;当这个控件的值发生变化的时候,就去触发上传的动作
在vue中的input标签中增加一个change
文件上传和文件储存(springboot+vue)_第4张图片
这个change的值时一个方法;然后在这个方法里做具体的跟后端的交互
文件上传和文件储存(springboot+vue)_第5张图片
首先要定义一个FormData,使用表单的方式来提交图片
在这里插入图片描述
往FormData里方图片
在这里插入图片描述
在input标签中增加一个id,值为file-upload-input(也就是上面的往FormData里传图片中的值)
在这里插入图片描述
文件上传和文件储存(springboot+vue)_第6张图片
以上步骤做完之后就拿到了图片
接着就把这张表单提交到后端
文件上传和文件储存(springboot+vue)_第7张图片
后端预留了以下的一个请求
文件上传和文件储存(springboot+vue)_第8张图片
还增加了show和hide的等待宽
文件上传和文件储存(springboot+vue)_第9张图片
最后拿到了结果
文件上传和文件储存(springboot+vue)_第10张图片

后端

新建一个Controller
文件上传和文件储存(springboot+vue)_第11张图片
接下来写upload方法
用@RequestParam来接收file变量
文件上传和文件储存(springboot+vue)_第12张图片
因为我们的文件是一个富文本,也就是图片;所以我们要用MultiparFile
文件上传和文件储存(springboot+vue)_第13张图片
返回值用ResponseDto
文件上传和文件储存(springboot+vue)_第14张图片

保存文件
保存文件用以下代码
出现异常要抛出去
文件上传和文件储存(springboot+vue)_第15张图片
接收传过来的文件FileName
文件上传和文件储存(springboot+vue)_第16张图片
并且生成一个8位的uuid(短id)
目的:防止同一张图片传多次,出现错误,加了uuid,这样就可传多个一样的图片
文件上传和文件储存(springboot+vue)_第17张图片
然后定义一个本地的路径
文件上传和文件储存(springboot+vue)_第18张图片
注释:如果不加短id,上传的文件名极可能重复,文件就会互相覆盖;
注意:文件名一样不代表文件就是一样的,因为有短id

接着用java自带的File生成一个目标位置dest
文件上传和文件储存(springboot+vue)_第19张图片
然后把vue传过来的file写到目标位置
文件上传和文件储存(springboot+vue)_第20张图片

 

SpringBoot+Vue表单文件上传

 

Spring Boot + Vue 的文件上传本身没有什么难点,但如果涉及到是一个表单对象中存在文件,则会比较繁琐

 

后端实体类

  1. Spring Boot中对于文件的接收类型和Spring MVC保持一致,使用MultipartFile
  2. 与Spring MVC不同的是Spring Boot进行文件上传操作不需要添加配置信息,Spring Boot自身已经默认开启了文件上传功能
...

public class Work {
  ...

    @Transient
    private MultipartFile referenceFile;

  ...
}

后端接收请求的接口

  1. SpringBoot与Vue进行集成,使用axios进行异步请求发送,对于接收对象类型的数据一般使用**@RequestBody**的注解将对象解析为JSON格式
  2. 但是MultipartFile类型的文件无法转换为JSON格式,所以此处需要使用**@ModelAttribute**的注解接收对象信息
...

@RestController
@RequestMapping("/${contextPath}/works")
public class WorkController extends SimpleController<Work, WorkService> {
  ...

    @ApiOperation("保存作业")
    @PostMapping("")
    public ResponseData saveRule(@ModelAttribute Work work) {
  return workService.save(work);
    }

  ...
}

前端对文件数据的处理

  1. 使用默认的文件输入框进行文件上传会影响美观,所以通常将输入框隐藏后通过点击按钮进行调用
  2. 由于文件格式的input属性是只读的,所以无法使用v-model对其数据的更改进行实时获取
  3. 所以需要通过**@change**对其数据的更改进行监听,并赋值给表单的对应属性
<template>
  <in-form ref="inForm" :form="work" :rules="rules" :is-file="true">
    ...

    <el-button type="success" v-else @click="uploadReferenceFile">
    上传答案 <span v-text="work.reference"></span>
    <input type="file" class="upload-file" ref="referenceFile" @change="setReferenceFile">
    </el-button>

    ...
  </in-form>
</template>

<script type="text/ecmascript-6">
...

  export default {
    ...

    methods: {
...

      // 上传参考答案
      uploadReferenceFile () {
        this.$refs.referenceFile.click()
      },
      // 设置参考答案
      setReferenceFile (item) {
        let currentFile = item.target.files[0]

        this.work.reference = currentFile.name
        this.work.referenceFile = currentFile
      }

...
  }
</script>

<style scoped lang="stylus" rel="stylesheet/stylus">
  .upload-file
    display none
</style>

前端发起请求的方式

  1. 文件格式的数据无法通过JSON格式进行传递,所以需要使用FormData对表单数据进行转换
  2. FormData只能接受StringFile格式的数据,对应Object格式的数据无法处理
  3. 如果涉及到Object格式的数据则需要前后端配合进行数据转换
  4. 通常情况下对象中关联的对象只涉及到其中的某一个值,所以前端在处理时可以单独将该值进行传递
  5. 后端在接收到该值后,可以在其Setter方法中将数据赋予对应的对象即可
export function save ({url, data}, isFile) {
  // 带文件的上传功能
  if (isFile) {
    let formData = new FormData()
    
    // 遍历传入的数据
    for (let key in data) {
      // 获取当前值
      let currentData = data[key]
  
      // 对于空值进行过滤
      if (currentData === '') {
        continue
      }
      
      // 将对象中的键值对传入formData中
      formData.append(key, currentData)
    }
    
    data = formData
  }
  
  return fetch({
    url: url,
    method: config.POST,
    data
  })
}

限制文件大小

  1. 只需要在::application.properties::中添加如下配置即可
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值