创建 FtpConfig 配置类

package com.jq.util;

public interface FtpConfig {

//ftp服务器ip地址
public static final String FTP_ADDRESS = "192.168.230.132";
//端口号
public static final int FTP_PORT = 21;
//用户名
public static final String FTP_USERNAME = "jin";
//密码
public static final String FTP_PASSWORD = "123";
//附件路径
public static final String FTP_BASEPATH = "/home/file/files";

//文件访问地址
public static final String FTP_FILE_PATH = "http://192.168.230.132:81/files/";

}

创建 FtpUtil 工具类

package com.jq.util;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

public class FtpUtil {



public static String uploadFile(MultipartFile file) throws IOException {
//获取上传的文件流
InputStream inputStream = file.getInputStream();

//获取上传的文件名
String filename = file.getOriginalFilename();
//截取后缀
String suffix = filename.substring(filename.lastIndexOf("."));
//使用UUID拼接后缀,定义一个不重复的文件名
String finalName = UUID.randomUUID()+suffix;

FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(FtpConfig.FTP_ADDRESS, FtpConfig.FTP_PORT);// 连接FTP服务器
ftp.login(FtpConfig.FTP_USERNAME, FtpConfig.FTP_PASSWORD);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return null;
}
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
ftp.makeDirectory(FtpConfig.FTP_BASEPATH);
ftp.changeWorkingDirectory(FtpConfig.FTP_BASEPATH);
ftp.enterLocalPassiveMode();
ftp.storeFile(finalName,inputStream);
inputStream.close();
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
return FtpConfig.FTP_FILE_PATH+finalName;
}
}

FileController 代码

package com.jq.controller;

import com.jq.util.FtpUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("file")
public class FileController {

//上传数据库
@RequestMapping("uploadPhoto")
public Map<String,Object> uploadPhoto(@RequestParam("file") MultipartFile imgpath, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
// 获取文件名
String filename = imgpath.getOriginalFilename();
// 得到新的文件名
String newFileName = UUID.randomUUID().toString() + filename.substring(filename.lastIndexOf("."));
// 获取上传路径
String path = request.getServletContext().getRealPath("/images");
// 判断有没有文件夹
File pathFile = new File(path);
if (!pathFile.exists()){
pathFile.mkdirs();
}
try {
// 调用spring提供的方法进行上传
File file = new File(path+"/"+newFileName);
imgpath.transferTo(file);
map.put("success",true);
map.put("imgurl","/images/"+newFileName);
} catch (IOException e) {
e.printStackTrace();
}

return map;
}

//上传服务器
@RequestMapping("ftpUpload")
public Map<String,Object>ftpUpload(@RequestParam("file") MultipartFile imgpath){
Map<String,Object>map=new HashMap<>();

try {
String fileName = FtpUtil.uploadFile(imgpath);
map.put("success",true);
map.put("imgurl",fileName);
} catch (IOException e) {
e.printStackTrace();
}
return map;
}


}

前端

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>商品管理</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="/resources/layui/css/layui.css" media="all" />
<link rel="stylesheet" href="/resources/css/public.css" media="all" />
<link rel="stylesheet" href="/resources/layui_ext/dtree/dtree.css">
<link rel="stylesheet" href="/resources/layui_ext/dtree/font/dtreefont.css">
</head>
<body class="childrenBody">
<blockquote class="layui-elem-quote quoteBox">
<form class="layui-form" id="searchForm">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品名称</label>
<div class="layui-input-inline">
<input type="text" id="goodsname" name="goodsname" autocomplete="off" class="layui-input" placeholder="商品名称" />
</div>
</div>

<div class="layui-inline">
<label class="layui-form-label">商品库存</label>
<div class="layui-input-inline">
<input type="text" id="dangernum" name="dangernum" autocomplete="off" class="layui-input" placeholder="请输入商品库存" style="width: 186px"/>
</div>
</div>

<div class="layui-inline" style="margin-left: 13px;">
<label class="layui-form-label">是否启用</label>
<div class="layui-input-block">
<input type="radio" name="available" value="1" title="启用" class="available">
<input type="radio" name="available" value="0" title="禁用" class="available">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">商品供应商</label>
<div class="layui-input-inline">
<select id="classes" name="providerid">
</select>
</div>
</div>



</div>

<div class="layui-form-item">
<div class="layui-input-block">
<!--<a class="layui-btn search_btn" data-type="reload">搜索</a>-->
<button type="submit" class="layui-btn layui-btn-sm" lay-submit="" lay-filter="search"><i class="layui-icon layui-icon-search"></i>搜索</button>

<button onclick="window.parent.frames[1].location.reload()" class="layui-btn layui-btn-sm layui-btn-primary"><i class="layui-icon layui-icon-refresh"></i>重置</button>
</div>
</div>

</form>
</blockquote>
<table id="goodsList" lay-filter="goodsList" class="layui-hide"></table>

<!--<script type="text/html" id="available">-->
<!--<input type="checkbox" name="available" value="{{d}}" lay-skin="switch" lay-text="启用|不启用" lay-filter="updateAvailable" {{ d.available == 1 ? 'checked' : '' }}>-->
<!--</script>-->

<!-- 行内工具栏 -->
<script type="text/html" id="goodsListBar">
<a class="layui-btn layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
</script>

<!-- 头部工具栏 -->
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm layui-btn" lay-event="addGoods"><i class="layui-icon layui-icon-add-circle-fine"></i>添加商品</button>
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="deleteBatch"><i class="layui-icon layui-icon-delete"></i>批量删除</button>
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="exportExcel"><i class="layui-icon layui-icon-export"></i>导出用户</button>
</div>
</script>

<!-- 添加和修改的弹出层开始 -->
<div style="display: none;padding: 10px" id="addOrUpdateDiv">
<form action="" method="post" class="layui-form layui-form-pane" id="dataForm" lay-filter="dataForm">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品名称</label>
<div class="layui-input-block">
<input type="hidden" id="id" name="id">
<input type="text" name="goodsname" lay-verify="required" autocomplete="off" placeholder="请输入商品名称" class="layui-input" style="width: 216px">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">生产地址</label>
<div class="layui-input-block">
<input type="text" name="produceplace" lay-verify="required" autocomplete="off" placeholder="请输入生产地址" class="layui-input" style="width: 216px">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品大小</label>
<div class="layui-input-block">
<input type="text" name="size" lay-verify="required" autocomplete="off" placeholder="请输入商品大小" class="layui-input" style="width: 216px">
</div>
</div>

<div class="layui-inline">
<label class="layui-form-label">商品包装</label>
<div class="layui-input-block">
<input type="text" name="goodspackage" lay-verify="required" autocomplete="off" placeholder="请输入商品包装" class="layui-input" style="width: 216px">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">生产编码</label>
<div class="layui-input-block">
<input type="text" name="productcode" lay-verify="required" autocomplete="off" placeholder="请输入生产编码" class="layui-input" style="width: 216px">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">优惠编码</label>
<div class="layui-input-block">
<input type="text" name="promitcode" lay-verify="required" autocomplete="off" placeholder="请输入优惠编码" class="layui-input" style="width: 216px">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品描述</label>
<div class="layui-input-block">
<input type="text" name="description" lay-verify="required" autocomplete="off" placeholder="请输入商品描述" class="layui-input" style="width: 216px">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">商品价格</label>
<div class="layui-input-block">
<input type="text" name="price" lay-verify="required" autocomplete="off" placeholder="请输入商品价格" class="layui-input" style="width: 216px">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品数量</label>
<div class="layui-input-block">
<input type="text" name="number" lay-verify="required" autocomplete="off" placeholder="请输入商品数量" class="layui-input" style="width: 216px">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">商品库存</label>
<div class="layui-input-block">
<input type="text" name="dangernum" lay-verify="required" autocomplete="off" placeholder="请输入商品库存" class="layui-input" style="width: 216px">
</div>
</div>
</div>


<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" >商品供应商</label>
<div class="layui-input-inline">
<select id="eduList" name="providerid" lay-search>
</select>
</div>
</div>
<div class="layui-inline" style="right: -20px">
<label class="layui-form-label">是否启用</label>
<div class="layui-input-inline">
<input type="radio" name="available" value="1" title="启用" checked>
<input type="radio" name="available" value="0" title="禁用">
</div>
</div>
</div>


<div class="layui-form-item">
<label class="layui-form-label">商品图片</label>
<div class="layui-upload-drag" id="test10">
<i class="layui-icon"></i>
<p>点击上传,或将文件拖拽到此处</p>
<div class="layui-hide" id="uploadDemoView">
<img src="" style="max-width: 196px">
<input type="hidden" id="imgpath" name="goodsimg"/>
</div>
</div>
</div>
</form>
</div>
<!-- 添加和修改的弹出层结束 -->


<script type="text/javascript" src="/resources/layui/layui.js"></script>
</body>
<script>
layui.extend({
dtree: '/resources/layui_ext/dtree/dtree' // {/}的意思即代表采用自有路径,即不跟随 base 路径
}).use(['form','layer','table','laytpl','laydate','dtree','upload'],function(){
var $=layui.jquery,
form=layui.form,
table=layui.table,
layer=layui.layer,
dtree=layui.dtree,
laydate = layui.laydate,
upload = layui.upload;


/*加载条件查询下拉*/
selectGoods("/goods/queryProvider","classes",null);

// 渲染日期
var startTime = laydate.render({
elem: '#startTime'
,type: 'datetime'
,calendar: true
,done:function (value,date) {
endTime.config.min = {
year: date.year,
month: date.month - 1,
date: date.date
}
}
});

var endTime = laydate.render({
elem: '#endTime'
,type: 'datetime'
,calendar: true
,done:function (value,date) {
startTime.config.max={
year: date.year,
month: date.month - 1,
date: date.date
}
}
});

// 商品列表
tableIns = table.render({
elem: '#goodsList', // 对应table的id
url : '/goods/queryGoods',
id : 'goodsListTable'
,toolbar: '#toolbarDemo' //开启头部工具栏
,page: { //支持传入 laypage 组件的所有参数(某些参数除外,如:jump/elem) - 详见文档
layout: ['limit', 'count', 'prev', 'page', 'next', 'skip'] //自定义分页布局
//,curr: 5 //设定初始在第 5 页
,first: '首页' //不显示首页
,last: '尾页' //不显示尾页
,limit:5 // 默认展示条数
,limits: [5,10,20,30] // 自定义选择条数

}
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [ [ //表头
{field: 'id', title: '序号',fixed: 'left', unresize: true,width: 120}
,{field: 'goodsname', title: '商品名称',width: 120}
,{field: 'goodsimg', title: '商品图片',width: 120,templet: function(res){
return "<img src='"+res.goodsimg+"' width='50'/>"
}}
,{field: 'produceplace', title: '生产地址',width: 120}
,{field: 'size', title: '商品大小',width: 120}
,{field: 'goodspackage', title: '商品包装',width: 150}
,{field: 'productcode', title: '生产编号',width: 120}
,{field: 'promitcode', title: '优惠编码',width: 150}
,{field: 'description', title: '商品描述',width: 120}
,{field: 'price', title: '商品价格',width: 150}
,{field: 'number', title: '商品数量',width: 200}
,{field: 'dangernum', title: '商品库存',width: 120}
/*
,{field: 'goodsimg', title: '商品图片',width: 120}
*/
,{field: 'available', title: '是否启用', width: 180, templet: function (data) {
if (data.available == 0) {
return '<div> <input type="checkbox" lay-skin="switch" name="available" switchId=' + data.id + ' lay-filter="updateAvailable"' +
'lay-text="禁用|启用" value=' + data.available + '></div>';
}

return '<div> <input type="checkbox" checked="" name="available" lay-skin="switch" id="open" lay-filter="updateAvailable" switchId=' + data.id + '' +
' lay-text="禁用|启用" value=' + data.available + '></div>';
}
}
,{field: 'providername', title: '商品供应商',width: 120}
,{fixed: 'right', title:'操作',toolbar: '#goodsListBar',width: 150}
] ],done: function(res, curr, count){
if (res.data.length == 0 && curr != 1){
tableIns.reload({
page: {
curr: curr-1
},
})
}
}
});

//是否启用
form.on('switch(updateAvailable)', function(data){
var layerIndex = layer.load();
$.get("/goods/updateAvaiable",{"id":data.elem.getAttribute("switchId"),"available": data.value},function (res) {
layer.msg(res.msg);
tableIns.reload();
layer.close(layerIndex);
});
})

// 搜索
form.on('submit(search)', function(data){


tableIns.reload({
where : data.field,
page: {
curr: 1 //重新从第 1 页开始
},
})
return false;
});

//监听行工具事件
table.on('tool(goodsList)', function(obj){
var layEvent = obj.event,
data = obj.data;
switch(layEvent) {
case 'del':
deleteGoods(data);
break;
case 'edit':
edit(data);
break;
case 'selectGoods':
selectGoods(data);
break;
}
});



//监听头工具栏事件
table.on('toolbar(goodsList)', function(obj){
var checkStatus = table.checkStatus(obj.config.id),
data = checkStatus.data; //获取选中的数据
switch(obj.event){
case 'addGoods':
addGoods();
break;
case 'deleteBatch':
deleteBatch(data);
break;
case 'exportExcel':
exportExcel();
break;
};
});


//查询外键
function selectGoods(url,select,id){
$.post({
url:url,
success:function (result) {
var list = result.list;
var html = "";
html+=" <option value='-1'>请选择商品供应商</option>";
for (var i = 0; i < list.length; i++) {
if (list[i].id == id){
html += "<option value='"+list[i].id+"' selected>"+list[i].providername+"</option>";
}else{
html += "<option value='"+list[i].id+"'>"+list[i].providername+"</option>";
}
}

$("#"+select).html(html);
form.render();
}
})
}

// 打开添加页面
function addGoods(){
$(':focus').blur();

layer.open({
type: 1,
title:"新增商品",
content: $("#addOrUpdateDiv"),
area:['70%','80%'],
success:function (layero,index) {
//进行下拉框的查询
selectGoods("/goods/queryProvider","eduList",null);

var html= "";
$("#leader").html(html);
//渲染
form.render();
//上传图片
layui.$('#uploadDemoView').removeClass('layui-hide').find('img').attr('src', '');
$('#imgpath').val();
}
,btn:["提交","取消"]
,yes:function (index,layero) {
$.ajax({
url:"/goods/addGoods",
type:"post",
data:$("#dataForm").serialize(),
success:function (result) {
if(result.code == 200){
layer.msg(result.msg);
// 关闭新增页面
layer.close(index);
// 重新加载表格数据
tableIns.reload();
}else{
layer.msg(result.msg);
}
},
error:function () {
layer.msg("新增失败,请联系系统管理员!");
}
})
},btn2:function () {
$("#dataForm")[0].reset();
}

})
}

//文件上传
//拖拽上传
upload.render({
elem: '#test10'
// ,url: '/file/ftpUpload'//数据库
,url: '/file/ftpUpload'//服务器
,done: function(res){
layer.msg('上传成功');
layui.$('#uploadDemoView').removeClass('layui-hide').find('img').attr('src', res.imgurl);
$('#imgpath').val(res.imgurl);
}
});
// 修改
function edit(data){
console.log(data);
layer.open({
type: 1,
title:"修改商品",
content: $("#addOrUpdateDiv"),
area:['70%','80%'],
success:function (layero,index) {
//图片
layui.$('#uploadDemoView').removeClass('layui-hide').find('img').attr('src', data.goodsimg);
$('#imgpath').val(data.goodsimg);


//回显
form.val('dataForm',data);
//回显
selectGoods("/goods/queryProvider","eduList",data.providerid);

}
,btn:["提交","取消"]
,yes:function (index,layero) {
$.ajax({
url:"/goods/editGoods",
type:"post",
data:$("#dataForm").serialize(),
success:function (result) {
$("#dataForm")[0].reset();
if(result.code == 200){
layer.msg(result.msg);
// 关闭新增页面
layer.close(index);
// 重新加载表格数据
tableIns.reload();
}else{
layer.msg(result.msg);
}
},
error:function () {
layer.msg("新增失败,请联系系统管理员!");
}
})
},btn2:function () {
$("#dataForm")[0].reset();
}

})
}


// 删除
function deleteGoods(data){
layer.confirm('确定删除这条商品吗?',{icon:3, title:'提示信息'},function(index){
$.post("/goods/deleteGoods",{
id : data.id,
},function(data){
if (data.code == 200){
layer.msg(data.msg);
tableIns.reload();
}else{
layer.msg(data.msg);
}
layer.close(index);
})
});
}


//导出
function exportExcel(){
var searchForm = document.getElementById("searchForm");
searchForm.action="/goods/exportExcel";
searchForm.submit();
}


// 批量删除
function deleteBatch(data){
console.log(data);
if (data.length > 0){
// 获取选中行的id
var ids = [];
for (var i = 0; i <data.length ; i++) {
ids.push(data[i].id);
}
layer.confirm('确定删除'+data.length+'条商品吗?',{icon:3, title:'提示信息'},function(index){
$.post("/goods/deleteBatch",{
ids : ids.toString(),
},function(data){
if (data.code == 200){
layer.msg(data.msg);
tableIns.reload();
}else{
layer.msg(data.msg);
}
layer.close(index);
})
});
}else{
layer.msg("请至少选择一行!");
}

}





})



</script>
</html>