文件上传就是将客户端文件复制到服务器的过程。只是上传很简单,但是要加上进度条等等条件就稍微有点复杂
@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"/> 文件描述: <input type="text" name="text'+(tabDW.rows.length+1)+'" /> <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" />
文件描述:
<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 库,那么一般来说,文件上传有两种不同的实现方案:
- 通过 Ajax 实现文件上传
- 通过 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);
}
})
}
关于这段上传核心逻辑,解释如下:
- 首先利用 Vue 中的 $refs 查找到存放文件的元素。
- type 为 file 的 input 元素内部有一个 files 数组,里边存放了所有选择的 file,由于文件上传时,文件可以多选,因此这里拿到的 files 对象是一个数组。
- 从 files 对象中,获取自己要上传的文件,由于这里是单选,所以其实就是数组中的第一项。
- 构造一个 FormData ,用来存放上传的数据,FormData 不可以像 Java 中的 StringBuffer 使用链式配置。
- 构造好 FromData 后,就可以直接上传数据了,FormData 就是要上传的数据。
- 文件上传注意两点,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>
- show-file-list 表示是否展示上传文件列表,默认为true,这里设置为不展示。
- before-upload 表示上传之前的回调,可以在该方法中,做一些准备工作,例如展示一个进度条给用户 。
- on-success 和 on-error 分别表示上传成功和失败时候的回调,可以在这两个方法中,给用户一个相应的提示,如果有进度条,还需要在这两个方法中关闭进度条。
- action 指文件上传地址。
- 上传按钮的点击状态和图标都设置为变量 ,在文件上传过程中,修改上传按钮的点击状态为不可点击,同时修改图标为一个正在加载的图标 loading。
- 上传的文本也设为变量,默认上传 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 = '正在导入';
}
- 在文件开始上传时,修改上传按钮为不可点击,同时修改上传按钮的图标和文本。
- 文件上传成功或者失败时,修改上传按钮的状态为可以点击,同时恢复上传按钮的图标和文本。
上传效果图如下:
总结
两种上传方式各有优缺点:
- 第一种方式最大的优势是通用,一招鲜吃遍天,到哪里都能用,但是对于上传过程的监控,进度条的展示等等逻辑都需要自己来实现。
- 第二种方式不够通用,因为它是 ElementUI 中的组件,得引入 ElementUI 才能使用,不过这种方式很明显有需多比较方便的回调,可以实现非常方便的处理常见的各种上传问题。
- 常规的上传需求第二种方式可以满足,但是如果要对上传的方法进行定制,则还是建议使用第一种上传方案。
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,使它变成一个文件组件
特点:文件组件没有数据绑定,否则页面会报错
vue中把文件传到后端去?
怎么触发这个动作(传到后端的这个动作)
方法一
增加按钮
方法二
直接在这个控件上增加一个事件监听;当这个控件的值发生变化的时候,就去触发上传的动作
在vue中的input标签中增加一个change
这个change的值时一个方法;然后在这个方法里做具体的跟后端的交互
首先要定义一个FormData,使用表单的方式来提交图片
往FormData里方图片
在input标签中增加一个id,值为file-upload-input(也就是上面的往FormData里传图片中的值)
以上步骤做完之后就拿到了图片
接着就把这张表单提交到后端
后端预留了以下的一个请求
还增加了show和hide的等待宽
最后拿到了结果
后端
新建一个Controller
接下来写upload方法
用@RequestParam来接收file变量
因为我们的文件是一个富文本,也就是图片;所以我们要用MultiparFile
返回值用ResponseDto
保存文件
保存文件用以下代码
出现异常要抛出去
接收传过来的文件FileName
并且生成一个8位的uuid(短id)
目的:防止同一张图片传多次,出现错误,加了uuid,这样就可传多个一样的图片
然后定义一个本地的路径
注释:如果不加短id,上传的文件名极可能重复,文件就会互相覆盖;
注意:文件名一样不代表文件就是一样的,因为有短id
接着用java自带的File生成一个目标位置dest
然后把vue传过来的file写到目标位置
SpringBoot+Vue表单文件上传
Spring Boot + Vue 的文件上传本身没有什么难点,但如果涉及到是一个表单对象中存在文件,则会比较繁琐
后端实体类
- Spring Boot中对于文件的接收类型和Spring MVC保持一致,使用MultipartFile
- 与Spring MVC不同的是Spring Boot进行文件上传操作不需要添加配置信息,Spring Boot自身已经默认开启了文件上传功能
...
public class Work {
...
@Transient
private MultipartFile referenceFile;
...
}
后端接收请求的接口
- SpringBoot与Vue进行集成,使用axios进行异步请求发送,对于接收对象类型的数据一般使用**@RequestBody**的注解将对象解析为JSON格式
- 但是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);
}
...
}
前端对文件数据的处理
- 使用默认的文件输入框进行文件上传会影响美观,所以通常将输入框隐藏后通过点击按钮进行调用
- 由于文件格式的input属性是只读的,所以无法使用v-model对其数据的更改进行实时获取
- 所以需要通过**@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>
前端发起请求的方式
- 文件格式的数据无法通过JSON格式进行传递,所以需要使用FormData对表单数据进行转换
- 但FormData只能接受String和File格式的数据,对应Object格式的数据无法处理
- 如果涉及到Object格式的数据则需要前后端配合进行数据转换
- 通常情况下对象中关联的对象只涉及到其中的某一个值,所以前端在处理时可以单独将该值进行传递
- 后端在接收到该值后,可以在其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
})
}
限制文件大小
- 只需要在::application.properties::中添加如下配置即可
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB