Gunakan SpringBoot untuk mengkonfigurasi pelayan FTP untuk memuat naik, memadam dan memuat turun fail.
Semak sama ada vsftpd dipasang
rpm -qa | grep vsftpd
Semak sama ada vsftpd dipasang dan semak nombor versi.
Pasang vsftpd
yum -y install vsftpd
Jika ralat dilaporkan, gunakan hak pentadbir untuk melaksanakan
sudo yum -y install vsftpd
Tutup akses tanpa nama
Selepas mematikan akses tanpa nama, anda memerlukan akaun dan kata laluan untuk mengakses fail di dalam jika anda tidak mematikannya, anda boleh mengaksesnya secara langsung.
vim /etc/vsftpd/vsftpd.conf
Jika gesaan ialah fail baca sahaja, maka anda hanya perlu memasukkan arahan:
sudo vim /etc/vsftpd/vsftpd.conf
seperti berikut:
rreeeMematikan akses tanpa nama bermakna : anonymous_enable=NO
Mulakan perkhidmatan
# Example config file /etc/vsftpd/vsftpd.conf # # The default compiled in settings are fairly paranoid. This sample file # loosens things up a bit, to make the ftp daemon more usable. # Please see vsftpd.conf.5 for all compiled in defaults. # # READ THIS: This example file is NOT an exhaustive list of vsftpd options. # Please read the vsftpd.conf.5 manual page to get a full idea of vsftpd's # capabilities. # # Allow anonymous FTP? (Beware - allowed by default if you comment this out). anonymous_enable=NO # # Uncomment this to allow local users to log in. local_enable=YES # # Uncomment this to enable any form of FTP write command. write_enable=YES # # Default umask for local users is 077. You may wish to change this to 022, # if your users expect that (022 is used by most other ftpd's) local_umask=022 # # Uncomment this to allow the anonymous FTP user to upload files. This only # has an effect if the above global write enable is activated. Also, you will # obviously need to create a directory writable by the FTP user. # When SELinux is enforcing check for SE bool allow_ftpd_anon_write, allow_ftpd_full_access #anon_upload_enable=YES # # Uncomment this if you want the anonymous FTP user to be able to create # new directories. #anon_mkdir_write_enable=YES # # Activate directory messages - messages given to remote users when they # go into a certain directory. dirmessage_enable=YES # # Activate logging of uploads/downloads. xferlog_enable=YES # # Make sure PORT transfer connections originate from port 20 (ftp-data). connect_from_port_20=YES # # If you want, you can arrange for uploaded anonymous files to be owned by # a different user. Note! Using "root" for uploaded files is not # recommended! #chown_uploads=YES #chown_username=whoever # # You may override where the log file goes if you like. The default is shown # below. #xferlog_file=/var/log/xferlog # # If you want, you can have your log file in standard ftpd xferlog format. # Note that the default log file location is /var/log/xferlog in this case. xferlog_std_format=YES # # You may change the default value for timing out an idle session. #idle_session_timeout=600
Lihat status perkhidmatan
systemctl start vsftpd.service
systemctl status vsftpd.service
Lihat yang hijau
active(running)
, yang bermaksud permulaan berjaya dan sedang berjalan.
Tambah pengguna FTP
Kerana di Linux, pengguna root tidak boleh log masuk ke FTP. Jika anda memasukkan pengguna root, log masuk akan gagal.
[root@hadoop-master ~]# systemctl status vsftpd.service ● vsftpd.service - Vsftpd ftp daemon Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; disabled; vendor preset: disabled) Active: active (running) since 一 2022-12-19 10:15:39 CST; 58min ago Process: 21702 ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf (code=exited, status=0/SUCCESS) Main PID: 21703 (vsftpd) CGroup: /system.slice/vsftpd.service └─21703 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf 12月 19 10:15:39 hadoop-master systemd[1]: Starting Vsftpd ftp daemon... 12月 19 10:15:39 hadoop-master systemd[1]: Started Vsftpd ftp daemon. [root@hadoop-master ~]#
Tetapkan kata laluan:
adduser ftpadmin
Masukkan kata laluan dua kali dan ia akan menjadi ok.
Konfigurasikan untuk membenarkan pengguna root log masuk
Ulas baris /etc/vsftpd/user_list
dalam fail /etc/vsftpd/ftpusers
dan root
fail
Ubah suai/etc/vsftpd/vsftpd.conf
, tambah local_root=/
passwd ftpadmin
pada baris terakhir supaya anda boleh log masuk dari jauh ke ftp sebagai pengguna akar.
Keizinan alamat storan fail
Jika alamat storan ialah: app/upload/
, tetapkan kebenaran kepada:
service vsftpd restart
Tambah kebergantungan
chmod 777 /app/upload/
Kelas alat pengendalian fail
<dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.55</version> </dependency>
UploadFileDto.java
package com.demo.utils; import com.jcraft.jsch.*; import com.demo.dto.UploadFileDto; import lombok.extern.slf4j.Slf4j; import java.io.File; import java.io.FileOutputStream; import java.util.Properties; /** * @ClassName: UploadFileUtils.java * @Description: 上传文件 * @Author: tanyp * @Date: 2022/12/19 10:38 **/ @Slf4j public class UploadFileUtils { /** * @MonthName: upload * @Description: 上传文件 * @Author: tanyp * @Date: 2022/12/19 10:38 * @Param: [dto] * @return: boolean **/ public static boolean upload(UploadFileDto dto) throws Exception { log.info("============上传文件开始=============="); Boolean result = false; ChannelSftp sftp = null; Channel channel = null; Session sshSession = null; try { JSch jSch = new JSch(); jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession.setPassword(dto.getPasswd()); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no"); sshSession.setConfig(sshConfig); sshSession.connect(); channel = sshSession.openChannel("sftp"); channel.connect(); sftp = (ChannelSftp) channel; sftp.cd(dto.getWorkingDir()); sftp.put(dto.getInputStream(), dto.getFileName()); result = true; log.info("============上传文件结束=============="); } catch (JSchException e) { result = false; log.error("=====上传文件异常:{}", e.getMessage()); e.printStackTrace(); } finally { closeChannel(sftp); closeChannel(channel); closeSession(sshSession); } return result; } /** * @MonthName: delete * @Description: 删除文件 * @Author: tanyp * @Date: 2022/12/19 10:38 * @Param: [dto] * @return: boolean **/ public static boolean delete(UploadFileDto dto) throws Exception { log.info("============删除文件开始=============="); Boolean result = false; ChannelSftp sftp = null; Channel channel = null; Session sshSession = null; try { JSch jSch = new JSch(); jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession.setPassword(dto.getPasswd()); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no"); sshSession.setConfig(sshConfig); sshSession.connect(); channel = sshSession.openChannel("sftp"); channel.connect(); sftp = (ChannelSftp) channel; sftp.cd(dto.getWorkingDir()); sftp.rm(dto.getFileName()); result = true; log.info("============删除文件结束=============="); } catch (JSchException e) { result = false; log.error("=====删除文件异常:{}", e.getMessage()); e.printStackTrace(); } finally { closeChannel(sftp); closeChannel(channel); closeSession(sshSession); } return result; } /** * @MonthName: download * @Description: 下载文件 * @Author: tanyp * @Date: 2022/12/19 10:38 * @Param: [dto] * @return: boolean **/ public static boolean download(UploadFileDto dto) throws Exception { log.info("============下载文件开始=============="); Boolean result = false; ChannelSftp sftp = null; Channel channel = null; Session sshSession = null; try { JSch jSch = new JSch(); jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); sshSession.setPassword(dto.getPasswd()); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no"); sshSession.setConfig(sshConfig); sshSession.connect(); channel = sshSession.openChannel("sftp"); channel.connect(); sftp = (ChannelSftp) channel; sftp.cd(dto.getWorkingDir()); sftp.get(dto.getFileName(), new FileOutputStream(new File(dto.getDownloadPath()))); sftp.disconnect(); sftp.getSession().disconnect(); result = true; log.info("============下载文件结束=============="); } catch (JSchException e) { result = false; log.error("=====下载文件异常:{}", e.getMessage()); e.printStackTrace(); } finally { closeChannel(sftp); closeChannel(channel); closeSession(sshSession); } return result; } private static void closeChannel(Channel channel) { if (channel != null) { if (channel.isConnected()) { channel.disconnect(); } } } private static void closeSession(Session session) { if (session != null) { if (session.isConnected()) { session.disconnect(); } } } }
UploadVo.java
UploadVo.java
package com.demo.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.InputStream; /** * @ClassName: UploadFileDto.java * @ClassPath: com.demo.dto.UploadFileDto.java * @Description: 上传文件 * @Author: tanyp * @Date: 2022/12/19 10:38 **/ @Data @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel(value = "上传文件Dto") public class UploadFileDto { @ApiModelProperty(value = " ftp 服务器ip地址") private String host; @ApiModelProperty(value = " ftp 服务器port,默认是21") private Integer port; @ApiModelProperty(value = " ftp 服务器用户名") private String account; @ApiModelProperty(value = " ftp 服务器密码") private String passwd; @ApiModelProperty(value = " ftp 服务器存储图片的绝对路径") private String workingDir; @ApiModelProperty(value = "上传到ftp 服务器文件名") private String fileName; @ApiModelProperty(value = " 文件流") private InputStream inputStream; @ApiModelProperty(value = " 下载文件的路径") private String downloadPath; }UploadController
package com.demo.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * @ClassName: UploadVo.java * @ClassPath: com.demo.vo.UploadVo.java * @Description: 文件VO * @Author: tanyp * @Date: 2022/12/19 15:18 **/ @Data @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel(value = "文件VO") public class UploadVo { @ApiModelProperty(value = "原始文件名称") private String oldName; @ApiModelProperty(value = "新文件名称") private String newName; @ApiModelProperty(value = "访问路径") private String path; }UploadService
package com.demo.controller; import com.demo.vo.UploadVo; import com.demo.service.UploadService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; /** * @ClassName: UploadController.java * @ClassPath: com.demo.controller.UploadController.java * @Description: 上传文件 * @Author: tanyp * @Date: 2022/12/19 15:18 **/ @Slf4j @RestController @RequestMapping("/upload") @Api(value = "upload", tags = "上传文件") public class UploadController { @Autowired private UploadService uploadService; @ApiOperation(value = "上传图片", notes = "上传图片") @PostMapping("/uploadImage") public UploadVo uploadImage(@RequestParam("file") MultipartFile file) { return uploadService.uploadImage(file); } @ApiOperation(value = "删除文件", notes = "删除文件") @GetMapping("/delFile") public Boolean delFile(String fileName) { return uploadService.delFile(fileName); } @ApiOperation(value = "下载文件", notes = "下载文件") @GetMapping("/downloadFile") public Boolean downloadFile(String fileName, String downloadPath) { return uploadService.downloadFile(fileName, downloadPath); } }UploadServiceImpl
package com.demo.service; import com.demo.vo.UploadVo; import org.springframework.web.multipart.MultipartFile; /** * @ClassName: UploadService.java * @ClassPath: com.demo.service.UploadService.java * @Description:上传文件 * @Author: tanyp * @Date: 2022/12/19 15:18 **/ public interface UploadService { UploadVo uploadImage(MultipartFile file); Boolean delFile(String fileName); Boolean downloadFile(String fileName, String downloadPath); }
package com.demo.service.impl; import com.demo.dto.UploadFileDto; import com.demo.vo.UploadVo; import com.demo.config.FtpConfig; import com.demo.service.UploadService; import com.demo.utils.UUIDUtils; import com.demo.utils.UploadFileUtils; import com.demo.exception.BusinessException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Objects; /** * @ClassName: UploadServiceImpl.java * @ClassPath: com.demo.service.impl.UploadServiceImpl.java * @Description: 上传文件 * @Author: tanyp * @Date: 2022/12/19 15:18 **/ @Slf4j @Service public class UploadServiceImpl implements UploadService { @Autowired private FtpConfig ftpConfig; @Override public UploadVo uploadImage(MultipartFile file) { log.info("=======上传图片开始,图片名称:{}", file.getOriginalFilename()); try { // 1. 取原始文件名 String oldName = file.getOriginalFilename(); // 2. ftp 服务器的文件名 String newName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UUIDUtils.getUUID(10) + oldName.substring(oldName.lastIndexOf(".")); // 3.上传图片 Boolean result = UploadFileUtils.upload( UploadFileDto.builder() .host(ftpConfig.host) .port(ftpConfig.post) .account(ftpConfig.username) .passwd(ftpConfig.password) .workingDir(ftpConfig.basePath) .fileName(newName) .inputStream(file.getInputStream()) .build() ); // 4.返回结果 if (!result) { throw new BusinessException("上传图片失败!"); } log.info("=======上传图片结束,新图片名称:{}", newName); return UploadVo.builder() .oldName(oldName) .newName(newName) .path(ftpConfig.imageBaseUrl + "/" + newName) .build(); } catch (Exception e) { log.error("=======上传图片异常,异常信息:{}", e.getMessage()); e.printStackTrace(); } return null; } @Override public Boolean delFile(String fileName) { if (Objects.isNull(fileName)) { throw new BusinessException("文件名称为空,请核实!"); } try { Boolean result = UploadFileUtils.delete( UploadFileDto.builder() .host(ftpConfig.host) .port(ftpConfig.post) .account(ftpConfig.username) .passwd(ftpConfig.password) .workingDir(ftpConfig.basePath) .fileName(fileName) .build() ); return result; } catch (Exception e) { log.error("=======删除文件异常,异常信息:{}", e.getMessage()); e.printStackTrace(); } return null; } @Override public Boolean downloadFile(String fileName, String downloadPath) { if (Objects.isNull(fileName) || Objects.isNull(downloadPath)) { throw new BusinessException("文件名称或下载路径为空,请核实!"); } try { Boolean result = UploadFileUtils.download( UploadFileDto.builder() .host(ftpConfig.host) .port(ftpConfig.post) .account(ftpConfig.username) .passwd(ftpConfig.password) .workingDir(ftpConfig.basePath) .fileName(fileName) .downloadPath(downloadPath) .build() ); return result; } catch (Exception e) { log.error("=======下载文件异常,异常信息:{}", e.getMessage()); e.printStackTrace(); } return null; } }
package com.demo.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @ClassName: FtpConfig.java * @ClassPath: com.demo.config.FtpConfig.java * @Description: FTP配置 * @Author: tanyp * @Date: 2022/12/19 22:28 **/ @Component public class FtpConfig { // ftp 服务器ip地址 @Value("${ftp.host}") public String host; // ftp 服务器port,默认是21 @Value("${ftp.post}") public Integer post; // ftp 服务器用户名 @Value("${ftp.username}") public String username; // ftp 服务器密码 @Value("${ftp.password}") public String password; // ftp 服务器存储图片的绝对路径 @Value("${ftp.base-path}") public String basePath; // ftp 服务器外网访问图片路径 @Value("${ftp.image-base-url}") public String imageBaseUrl; }application.yml
# ftp ftp: host: 127.0.0.1 post: 22 username: ftpadmin password: ftpadmin base-path: /app/upload/images image-base-url: http://127.0.0.1:8080/images
server { listen 8080; server_name localhost; location /images/ { root /app/upload/; autoindex on; } }
Atas ialah kandungan terperinci Bagaimana SpringBoot menggunakan FTP untuk mengendalikan fail. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!