前言
- 上传:把文件上传到服务器,服务器ip+端口+请求路径;
- 下载:从服务器下载文件,服务器ip+端口+请求路径;
- springboot中配置数据库连接的用户名和密码是服务器数据库的用户名和密码;
- 前端不要配置axios请求的baseurl,那是开发时候用的,打包项目必须注释;
- vue的项目打包后dist下的文件放在springboot项目中的static下,package后,直接访问服务器ip+端口,就能访问项目,此时浏览器中会自动在每个请求前加上该服务器的本机ip地址;因此其他客户端也就能直接访问服务器ip+端口访问服务器上部署的项目;
一、第一段弯路(简单记录前端代码)
主要原因是不知道el-upload如何将文件和表单同时提交。功能虽然实现,但有点耍流氓。
- 先添加form表单数据;
- 再单独上传文件(在文件上传时将文件名截取去掉后缀作为另一个表的version,为了建立两表的联系,根据version下载文件);
- 为了能够下载文件,必须将文件命名为跟版本号相同,因为下载是根据版本号下载的; 数据库中使用了两张表;
- 使用这种流氓方法,表中的版本号如果重复,下载的时候一定报错,sql查出来多条下载路径,因此需要在业务层判断上传的该版本是否已存在,有则先删除,否则直接插入数据。
嗯,真流氓!
<el-button type="primary" size="mini" @click="openUpload()">
<i class="el-icon-upload"></i>
<span>上传apk</span>
</el-button>
// 打开文件上传的窗口
openUpload() {
this.$refs["Upload"].UploadVisible = true;
},
el-dialog:
:action="url" ( let url = "upload/uploadInfo";)
:auto-upload="true"
:data="param"
:file-list="fileList"
multiple
accept=".apk,.png,.jpg,.xlsx"
:beforeUpload="beforeAvatarUpload"
<el-dialog
title="Apk上传"
:visible.sync="UploadVisible"
width="40%"
size="samll"
>
<div>
<el-upload
style="margin-left: 15%"
class="upload-demo"
:action="url"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-change="changeFile"
:on-success="onSuccess"
:onError="onError"
:data="param"
:file-list="fileList"
:beforeUpload="beforeAvatarUpload"
accept=".apk,.png,.jpg,.xlsx"
:auto-upload="true"
:drag="true"
multiple
>
<el-button
slot="trigger"
size="small"
type="primary"
style="margin-top: 70px"
>
选取文件
</el-button>
</el-upload>
<div slot="tip" class="el-upload__tip" style="margin-top: 20px">
点击或拖拽上传apk类型的文件,<b
>文件命名必须和列表安卓版本号相同,否则无法下载对应版本apk</b
>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button
type="primary"
@click="UploadVisible = false"
size="small"
style="margin-right: 20px"
>关闭</el-button
>
</span>
</el-dialog>
beforeAvatarUpload(file) {
this.param.version = this.splitFileName(file.name);
// 文件名
this.param.fileName = file.name;
// 文件大小
// this.param.fileSize = Math.round(file.size / 1024) + "KB";
this.param.fileSize = file.size;
// 文件url
this.param.url = this.url + "/" + this.splitFileName(file.name);
//文件上传人
this.param.uploadPerson = window.localStorage.getItem("WelcomeName");
if (file.size > this.limitSize) {
this.$message({
message: "文件超出大小限制",
type: "warning",
});
return false;
}
return true;
},
//去掉文件后缀
splitFileName(text) {
var pattern = /\.{1}[a-z]{1,}$/;
if (pattern.exec(text) !== null) {
return text.slice(0, pattern.exec(text).index);
} else {
return text;
}
},
二、第二段弯路(简单记录前端代码)
后来在csdn里面看到一段关于文件和表单同时提交的案例,只不过前端用的是H5的input标签,设置类型type=file。后来使用这种方法确实解放了用户,不限制文件名,也使用了一张数据库表,但是这种原生的标签,缺点在于每次只能上传一个文件,如果想同时上传多个文件,必须自己写代码处理。
关键:let formData = new FormData();
<input
id="upLoad"
v-if="this.option === 'add'"
accept=".xls,.xlsx,.apk,.png,.PNG,.jpg,.JPG,.doc,.docx,.pdf,.txt"
type="file"
name="file"
@change="getFile($event)"
style="margin-left: 90px; margin-bottom: 10px"
/>
//获取上传文件
getFile(event) {
this.file = event.target.files[0];
},
// 添加版本信息
event.preventDefault(); //取消默认行为
let formData = new FormData();
formData.append("apkName", this.versionForm.apkName);
formData.append("version", this.versionForm.version);
formData.append("update_type", this.versionForm.update_type);
formData.append("update_info", this.versionForm.update_info);
formData.append("update_person", this.versionForm.update_person);
formData.append("fit_mainBoard", this.versionForm.fit_mainBoard);
formData.append("fw_version", this.versionForm.fw_version);
formData.append("file", this.file);
let res = await this.$http.post("version/addVersion", formData);
if (res.status === 200 && res.data.result === "success") {
this.$message.success("添加成功");
//调用父组件函数,刷新列表
this.$parent.getVersionList();
this.$parent.getTotal(); //新增后更新total
this.VersionFormVisible = false;
var obj = document.getElementById("upLoad");
obj.value = "";
} else {
this.$message.error("添加失败,请稍后重试");
}
三、正确打开方式(全部代码)
来了,它来了,它真的来了,它带着帅气走来了,他就是vue
前端关闭自动提交,不要绑定action到url;
通过:http-request自定义请求。
ref="upload"
action="string"
:data="param"
:file-list="fileList"
:beforeUpload="beforeAvatarUpload"
:http-request="handleUpload"
accept=".apk,.png,.jpg,.xlsx"
:auto-upload="false"
在提交的时候
// 添加版本信息
this.$refs.upload.submit();
submit方法就会调用http-request绑定的自定义方法handleUpload(param)
,注意这里的参数一定是param,上传的文件就是param.file
。
值得一提的是:绑定到一个数组:file-list="fileList"
,就可以提交一个或多个文件,不需要做任何处理。
好了,下面正式开始:
上传前端
<template>
<div>
<el-dialog :title="title" width="40%" :visible.sync="VersionFormVisible">
<el-form
:model="versionForm"
ref="versionFormRef"
:rules="versionFormRules"
label-width="90px"
>
<el-form-item label="软件名称" prop="apkName">
<el-input v-model="versionForm.apkName"></el-input>
</el-form-item>
<el-form-item label="版本号" prop="version">
<el-input v-model="versionForm.version"></el-input>
</el-form-item>
<el-form-item label="更新人" prop="update_person">
<el-input v-model="versionForm.update_person"></el-input>
</el-form-item>
<el-form-item label="适配主板" prop="fit_mainBoard">
<el-input v-model="versionForm.fit_mainBoard"></el-input>
</el-form-item>
<el-form-item label="固件版本" prop="fw_version">
<el-input v-model="versionForm.fw_version"></el-input>
</el-form-item>
<el-form-item label="更新类型" prop="update_type">
<el-select
v-model="versionForm.update_type"
placeholder="请选择更新类型"
>
<el-option label="一般更新" value="一般更新"></el-option>
<el-option label="中级更新" value="中级更新"></el-option>
<el-option label="严重BUG更新" value="严重BUG更新"></el-option>
</el-select>
</el-form-item>
<el-form-item label="文件上传" v-if="this.option === 'add'">
<!--v-if="this.option==='add'" 只在添加版本信息的时候出现文件上传按钮 -->
<el-upload
ref="upload"
action="string"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-change="changeFile"
:on-success="onSuccess"
:onError="onError"
:data="param"
:file-list="fileList"
:beforeUpload="beforeAvatarUpload"
:http-request="handleUpload"
accept=".apk,.png,.jpg,.xlsx"
:auto-upload="false"
>
<el-button slot="trigger" size="small" type="primary"
>选取文件</el-button
>
<span slot="tip" class="el-upload__tip" style="margin-left: 10px"
><i>请上传文件(支持单文件和多文件上传),否则无法提交!</i></span
>
</el-upload>
</el-form-item>
<el-form-item label="更新描述" prop="update_info">
<textarea
v-model="versionForm.update_info"
style="
min-height: 180px;
font-size: 14px;
min-width: 410px;
margin-top:10px;
"
></textarea>
</el-form-item>
</el-form>
<div
slot="footer"
class="dialog-footer"
style="margin-top: -50px; margin-right: 25px"
>
<el-button @click="reset()" size="mini">取 消</el-button>
<el-button type="primary" size="mini" @click="submit()"
>确 定</el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "VersionForm",
data() {
return {
title: "",
VersionFormVisible: false,
versionForm: {},
versionFormRules: {
apkName: [
{ required: true, message: "请输入软件名称", trigger: "blur" },
],
version: [{ required: true, message: "请输入版本号", trigger: "blur" }],
update_type: [
{ required: true, message: "请选择更新类型", trigger: "blur" },
],
update_info: [
{ required: true, message: "请输入更新描述信息", trigger: "blur" },
],
update_person: [
{ required: true, message: "请输入更新人名称", trigger: "blur" },
],
fit_mainBoard: [
{ required: true, message: "请输入适配主板", trigger: "blur" },
],
fw_version: [
{ required: true, message: "请输入固件版本", trigger: "blur" },
],
},
option: "",
fileList: [],
limitSize: 1024 * 1 * 1024000,
param: {},
};
},
methods: {
reset() {
this.VersionFormVisible = false;
this.$refs.versionFormRef.resetFields();
this.fileList=[];
},
editInitData(row) {
this.title = "编辑版本信息";
this.option = "edit";
this.versionForm = {
id: row.id,
apkName: row.apkName,
version: row.version,
update_type: row.update_type,
update_info: row.update_info,
update_person: row.update_person,
fit_mainBoard: row.fit_mainBoard,
fw_version: row.fw_version,
};
},
addInitData() {
this.title = "添加版本信息";
this.option = "add";
this.versionForm = {
apkName: "",
version: "",
update_type: "",
update_info: "",
update_person: "",
};
},
//发起编辑和添加请求
submit() {
this.$refs.versionFormRef.validate(async (valid) => {
if (!valid) return;
if (this.option === "edit") {
// 编辑版本信息
let param = {
id: this.versionForm.id,
apkName: this.versionForm.apkName,
version: this.versionForm.version,
update_type: this.versionForm.update_type,
update_info: this.versionForm.update_info,
update_person: this.versionForm.update_person,
fit_mainBoard: this.versionForm.fit_mainBoard,
fw_version: this.versionForm.fw_version,
};
let res = await this.$http.post(
"version/editVersion",
this.$qs.stringify(param)
);
if (res.status === 200 && res.data.result === "success") {
this.$message.success("编辑成功");
//调用父组件函数,刷新列表
this.$parent.getVersionList();
this.VersionFormVisible = false;
} else {
this.$message.error("编辑失败,请稍后重试");
}
} else {
// 添加版本信息
this.$refs.upload.submit();
}
});
},
handleRemove() {},
handlePreview() {},
changeFile() {},
onSuccess(response, file) {
this.$message({
message: `${file.name} 上传成功`,
type: "success",
duration:1500
});
},
onError(response, file) {
this.$message({
message: `${file.name} 上传失败`,
type: "warning"
});
},
beforeAvatarUpload(file) {
if (file.size > this.limitSize) {
this.$message({
message: "文件超出大小限制",
type: "warning",
});
return false;
}
return true;
},
// 注意:param
async handleUpload(param) {
this.VersionFormVisible = false;
this.$message({
type: "success",
message: `【${param.file.name}】`+"已经开始上传",
duration: 1500,
});
let formData = new FormData();
formData.append("apkName", this.versionForm.apkName);
formData.append("version", this.versionForm.version);
formData.append("update_type", this.versionForm.update_type);
formData.append("update_info", this.versionForm.update_info);
formData.append("update_person", this.versionForm.update_person);
formData.append("fit_mainBoard", this.versionForm.fit_mainBoard);
formData.append("fw_version", this.versionForm.fw_version);
formData.append("file", param.file);
let res = await this.$http.post("version/addVersion", formData);
if (res.status === 200 && res.data.result === "success") {
this.$message({
type: "success",
message: `【${this.versionForm.apkName}】`+"版本信息添加成功",
duration: 3000,
});
//调用父组件函数,刷新列表
this.$parent.getVersionList();
this.$parent.getTotal(); //新增后更新total
this.fileList = [];
} else {
this.$message.error("版本信息添加失败,请稍后重试");
}
},
},
};
</script>
<style lang="less" scoped>
.el-form-item {
margin-right: 20px;
}
</style>
上传后端
//增加版本信息
@RequestMapping(value = "/addVersion",method = RequestMethod.POST)
@ResponseBody
public Message addVersion(AddVersionParam addVersionParam,@RequestParam("file") MultipartFile file){
String originalFilename=file.getOriginalFilename();
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String filePath=System.getProperty("user.dir")+"\\upload\\";
File Dir = new File(filePath);
if (!Dir.exists()) {
Dir.mkdir();
}
filePath += sdf.format(date) + "\\";
Dir = new File(filePath);
if (!Dir.exists()) {
Dir.mkdir();
}
File dest=new File(filePath+originalFilename);
try {
file.transferTo(dest); //使用transferTo保存文件
addVersionParam.setFileName(originalFilename);
addVersionParam.setFilePath("upload\\"+sdf.format(date)+"\\"+originalFilename);
versionService.addVersion(addVersionParam);
return onSuccess("上传成功");
} catch (Exception e) {
e.printStackTrace();
}
return onError("上传失败");
}
下载前端
<el-button size="mini" type="warning" @click="downLoad(scope.row)"
>apk下载</el-button
>
//下载apk
downLoad(row) {
const loading = this.$loading({
lock: true,
text: "下载中,时间较长,完成后会弹出提示,请注意查看",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)",
});
setTimeout(function () {
loading.close();
}, 3000);
window.open("version/downLoad/" + row.id, "_self");
},
下载后端
//apk下载
@RequestMapping(value = "/downLoad/{id}", method = RequestMethod.GET)
@ResponseBody
public Message downLoad(@PathVariable Integer id , HttpServletResponse response) throws UnsupportedEncodingException {
File file = new File(System.getProperty("user.dir")+"\\"+versionService.getFilePath(id));
if(file.exists()){
// 设置response的Header请求头
response.addHeader("Content-Disposition", "attachment;filename=" + new String(versionService.getFileName(id).getBytes("UTF-8"), "ISO-8859-1"));
response.addHeader("Content-Length", "" +file.length());
byte[] buffer = new byte[1024];
FileInputStream fis = null; //文件输入流
BufferedInputStream bis = null;
OutputStream os = null; //输出流
try {
os = response.getOutputStream();
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
int i = bis.read(buffer);
while(i != -1){
os.write(buffer);
i = bis.read(buffer);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
bis.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return onError("下载失败");
}
上传和下载,对于路径:System.getProperty("user.dir")
这点需要特别注意。
完!