目录
我的需求:
1.搭建完ftp后,添加一个ftp用户,只允许对指定的目录进行读写操作,其余目录禁止读写.
2.在java代码中使用ftp用户账户,进行上传文件/图片.
3.使用nginx整合ftp,提供对外访问文件/图片的url路径.
在java中使用ftp上传时,有关于目录的问题,参考这篇文章:https://blog.csdn.net/qq_23167527/article/details/66975727
1.安装过程:
第一步:检查是否安装了vsftpd
rpm -qa | grep vsftpd
第二步:如果没有安装则安装vsftpd
yum -y install vsftpd
第三步: 补充知识点,不需要全部执行
systemctl status vsftpd 查看服务状态
systemctl start vsftpd 启动服务
systemctl stop vsftpd 暂停服务
systemctl restart vsftpd 重启启动服务
systemctl enable vsftpd 开机自启动
systemctl disable vsftpd 取消开机自启动
附带图:
2.开启远程访问
在阿里云安全组中,添加安全组规则
3.修改配置文件,禁止匿名访问
编辑配置文件
vim /etc/vsftpd/vsftpd.conf
默认是yes将其改为no
anonymous_enable=NO
4.创建一个用户
创建一个用户使用ftp
useradd ftpuser -d /home/ftpimage
设置用户密码,输入两次就行
passwd ftpuser
5.开启被动模式
被动模式默认是开启的,但是需要指定一个端口范围.
vim /etc/vsftpd/vsftpd.conf
在最后面加上,注意这里是开启范围端口,意思是说,8800~8899这个范围内的端口必须全部开启,当然你也可以自己随便设置
pasv_min_port=8800
pasv_max_port=8899
表示端口范围为8800~8899,这个可以随便修改,改完记得重启一下vsftpd服务。
友情提示:最大和最小,最好设置为一样,这样就成了固定端口了
对应的我们也应该在安全组策略中添加这个范围的端口。
6.配置用户ftp权限及访问路径限制
编辑配置文件,在末尾添加,这三行
vim /etc/vsftpd/vsftpd.conf
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd/user_list
编辑user_list文件.将用户名添加进去
ftpuser
这时候发现,创建的用户是可以访问其他路径的,如果想要不允许,则编辑配置文件,将这两行前的#号去掉
chroot_list_enable=YES
# (default follows)
chroot_list_file=/etc/vsftpd/chroot_list
如果没有chroot_list文件,则手动创建
touch chroot_list
编辑chroot_list文件,将用户账号添加进去
如果发现vsftpd: refusing to run with writable root inside chroot错误
原因:从2.3.5之后,vsftpd增强了安全检查,如果用户被限定在了其主目录下,则该用户的主目录不能再具有写权限了!如果检查发现还有写权限,就会报该错误
解决:编辑配置文件,在末尾加上
allow_writeable_chroot=YES
如果发现上传不了文件,则需要设置文件夹读写权限
碰见的问题:
1.dos命令能连接,点击查看这篇文章: FileZilla MLSD错误:连接超时、读取目录列表失败
2.dos命令能连接,但是windows连接不上,直接修改被动模式的端口,将最大端口和最小端口修改为一样
整合nginx
1.安装nginx参考这篇文章:https://www.cnblogs.com/wyd168/p/6636529.html
2.修改nginx配置文件
访问效果:
如果这里访问报404,则需要考虑指定的目录是否有可访问权限
导入依赖:
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>
实体类:
public class PicUploadResult {
private int error;
/**
* 图片保存路径
*/
private String url;
// set/get......
}
java上传工具类:
import com.bbpk.common.base.ConstantInterface;
import com.bbpk.common.base.entity.vo.PicUploadResult;
import com.xiaoleilu.hutool.date.DateTime;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 文件上传帮助类
*/
public class FileUploadUtil {
public static boolean isImage(String contentType) {
for (String type : ConstantInterface.IMAGE_CONTENT_TYPE) {
if (contentType.toLowerCase().equals(type)) {
return true;
}
}
return false;
}
/**
* 照片上传
*
* @param uploadFile 需上传的文件
* @return
* @throws Exception
*/
public static PicUploadResult imgUpload(MultipartFile uploadFile) throws Exception {
if (null == uploadFile) return null;
// 封装Result对象,并且将文件的byte数组放置到result对象中
PicUploadResult fileUploadResult = new PicUploadResult();
// 校验图片格式
// boolean isLegal = isImageFile(uploadFile.getOriginalFilename());
boolean isLegal = isImage(uploadFile.getContentType());
// 状态
fileUploadResult.setError(isLegal ? 0 : 1);
// 文件新路径
String fileName = getFileName(uploadFile.getContentType());
File newFile = new File(fileName);
InputStream is = uploadFile.getInputStream();
if (null == is) {
throw new Exception("图片输入流为获取失败");
}
// 图片上传到FTP服务器
boolean rtn = FTPClientUtil.uploadImage("", newFile.getName(), is);
fileUploadResult.setUrl("http://IP+端口/"+newFile.getName());
fileUploadResult.setError(isLegal && rtn ? 0 : 1);
return fileUploadResult;
}
public static String getFileName(String contentType) {
String suffix = "";
List<String> type = Arrays.stream(ConstantInterface.IMAGE_CONTENT_TYPE)
.filter(n -> n.equals(contentType))
.collect(Collectors.toList());
if (type.size() > 0) {
suffix = StringUtils.substringAfterLast(type.get(0), "/");
}
Date nowDate = new Date();
return new DateTime(nowDate).toString("yyyyMMddhhmmssSSSS")
+ RandomUtils.nextInt(100, 9999) + "." + suffix;
}
}
import com.bbpk.common.base.ConstantInterface;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
/**
* FTP客户端 上传文件
*/
public class FTPClientUtil {
/**
* 校验图片格式
*
* @param filename
* @return
*/
public static boolean isImageFile(String filename) {
// 校验图片格式
boolean isLegal = false;
for (String type : ConstantInterface.IMAGE_TYPE) {
if (org.apache.commons.lang3.StringUtils.endsWithIgnoreCase(filename, type)) {
isLegal = true;
break;
}
}
return isLegal;
}
/**
* 获取FTP连接
*
* @return
*/
private static FTPClient getInstance() {
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect("ip地址", 21);//连接FTP服务器
//如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
boolean rtn = ftp.login("账户", "密码");//登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
return ftp;
}
/**
* 上传图片到服务器
*
* @param relativePath 相对路径
* @param filename 文件名
* @param input 文件流
* @return 成功返回true,否则返回false
*/
public static boolean uploadImage(String relativePath, String filename, InputStream input) {
return FTPClientUtil.uploadFile("/home/monkeyimg/img" + relativePath, filename, input);
}
/**
* 向FTP服务器上传文件
*
* @param path FTP服务器保存目录
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String path, String filename, InputStream input) {
boolean success = false;
FTPClient ftpClient = getInstance();
ftpClient.enterLocalPassiveMode();
try {
ftpClient.setControlEncoding("UTF-8");
ftpClient.changeWorkingDirectory(path);
//如果是图片 需要修改上传文件的格式,否则图片以文本的形式传输,会失真
//ftpClient默认上传的格式是文本
//修改成二进制上传
if (isImageFile(filename)) {
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
}
//注意注意注意:需要设置目录权限为777 坑死我了
success = ftpClient.storeFile(new String(filename.getBytes("UTF-8"), "iso-8859-1"), input);
input.close();
ftpClient.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException ioe) {
}
}
}
return success;
}
}