当前位置: 移动技术网 > IT编程>开发语言>Java > 使用sftp操作文件并添加事务管理

使用sftp操作文件并添加事务管理

2019年03月09日  | 移动技术网IT编程  | 我要评论

  本文主要针对文件操作的事务管理,即写文件和删除文件并且能保证事务的一致性,可与数据库联合使用,比如需要在服务器存文件,相应的记录存放在数据库,那么数据库的记录和服务器的文件数一定是要一一对应的,该部分代码可以保证大多数情况下的文件部分的事务要求(特殊情况下面会说),和数据库保持一致的话需要自行添加数据库部分,比较简单。

  基本原理就是,添加文件时先在目录里添加一个临时的文件,如果失败或者数据库插入部分失败直接回滚,即删除该文件,如果成功则提交事务,即将该文件重命名为你需要的正式文件名字(重命名基本不会失败,如果失败了比如断电,那就是特殊情况了)。同理删除文件是先将文件重命名做一个临时文件而不是直接删除,然后数据库部分删除失败的话回滚事务,即将该文件重命名成原来的,如果成功则提交事务,即删除临时文件。

  和数据库搭配使用异常的逻辑判断需要谨慎,比如删除文件应先对数据库操作进行判断,如果先对文件操作进行判断,加入成功了直接提交事务即删除了临时文件,数据库部分失败了文件是没办法回滚的。

我这里用的是spriingboot,如果用的别的看情况做修改即可,这里需要四个类:

sftpproperties这个是sftp连接文件服务器的各项属性,各属性需要配置到springboot配置文件中,也可以换种方法获取到即可。

 1 import org.springframework.beans.factory.annotation.value;
 2 import org.springframework.stereotype.component;
 3 
 4 @component
 5 public class sftpproperties {
 6     @value("${spring.sftp.ip}")
 7     private string ip;
 8     @value("${spring.sftp.port}")
 9     private int port;
10     @value("${spring.sftp.username}")
11     private string username;
12     @value("${spring.sftp.password}")
13     private string password;
14 
15     public string getip() {
16         return ip;
17     }
18 
19     public void setip(string ip) {
20         this.ip = ip;
21     }
22 
23     public int getport() {
24         return port;
25     }
26 
27     public void setport(int port) {
28         this.port = port;
29     }
30 
31     public string getusername() {
32         return username;
33     }
34 
35     public void setusername(string username) {
36         this.username = username;
37     }
38 
39     public string getpassword() {
40         return password;
41     }
42 
43     public void setpassword(string password) {
44         this.password = password;
45     }
46 
47     @override
48     public string tostring() {
49         return "sftpconfig{" +
50                 "ip='" + ip + '\'' +
51                 ", port=" + port +
52                 ", username='" + username + '\'' +
53                 ", password='******'}";
54     }
55 }

sftpclient:这个主要通过sftp连接文件服务器并读取数据。

 1 import com.jcraft.jsch.*;
 2 import org.slf4j.logger;
 3 import org.slf4j.loggerfactory;
 4 import org.springframework.stereotype.component;
 5 
 6 import java.io.*;
 7 
 8 @component
 9 public class sftpclient implements autocloseable {
10     private static final logger logger = loggerfactory.getlogger(sftpclient.class);
11     private session session;
12 
13     //通过sftp连接服务器
14     public sftpclient(sftpproperties config) throws jschexception {
15         jsch.setconfig("stricthostkeychecking", "no");
16         session = new jsch().getsession(config.getusername(), config.getip(), config.getport());
17         session.setpassword(config.getpassword());
18         session.connect();
19     }
20 
21     public session getsession() {
22         return session;
23     }
24 
25     public channelsftp getsftpchannel() throws jschexception {
26         channelsftp channel = (channelsftp) session.openchannel("sftp");
27         channel.connect();
28         return channel;
29     }
30 
31     /**
32      * 读取文件内容
33      * @param destfm 文件绝对路径
34      * @return
35      * @throws jschexception
36      * @throws ioexception
37      * @throws sftpexception
38      */
39     public byte[] readbin(string destfm) throws jschexception, ioexception, sftpexception {
40         channelsftp channel = (channelsftp) session.openchannel("sftp");
41         channel.connect();
42         try (bytearrayoutputstream outputstream = new bytearrayoutputstream()) {
43             channel.get(destfm, outputstream);
44             return outputstream.tobytearray();
45         } finally {
46             channel.disconnect();
47         }
48     }
49 
50     /**
51      * 退出登录
52      */
53     @override
54     public void close() throws exception {
55         try {
56             this.session.disconnect();
57         } catch (exception e) {
58             //ignore
59         }
60     }
61 }

sftptransaction:这个主要是对文件的操作

  1 import com.jcraft.jsch.channelsftp;
  2 import com.jcraft.jsch.jschexception;
  3 import org.apache.commons.lang.stringutils;
  4 import org.apache.commons.lang3.tuple.pair;
  5 import org.slf4j.logger;
  6 import org.slf4j.loggerfactory;
  7 import org.springframework.stereotype.component;
  8 
  9 import java.io.bytearrayinputstream;
 10 import java.util.arraylist;
 11 import java.util.list;
 12 import java.util.uuid;
 13 
 14 @component
 15 public class sftptransaction {
 16     private static final logger logger = loggerfactory.getlogger(sftptransaction.class);
 17     private final string transactionid;  // 事务唯一id
 18     private final channelsftp channelsftp;
 19     private int optype = -1;  // 文件操作标识 1 添加文件  2 删除文件
 20     private list<string> opfiles = new arraylist<>(5);
 21 
 22     public sftptransaction(sftpclient client) throws jschexception {
 23         this.transactionid = stringutils.replace(uuid.randomuuid().tostring(), "-", "");
 24         this.channelsftp = client.getsftpchannel();
 25     }
 26 
 27     // 根据文件名和事务id创建临时文件
 28     private string transactionfilename(string transactionid, string filename, string path) {
 29         return string.format("%stransact-%s-%s", path, transactionid, filename);
 30     }
 31 
 32     // 根据路径反推文件名
 33     private string untransactionfilename(string tfm, string path) {
 34         return path + stringutils.split(tfm, "-", 3)[2];
 35     }
 36 
 37     /**
 38      * 添加文件
 39      * @param contents 存放文件内容
 40      * @param path 文件绝对路径(不包含文件名)
 41      * @throws exception
 42      */
 43     public void create(list<pair<string, byte[]>> contents, string path) throws exception {
 44         if (this.optype == -1) {
 45             this.optype = 1;
 46         } else {
 47             throw new illegalstateexception();
 48         }
 49         for (pair<string, byte[]> content : contents) {
 50             // 获取content里的数据
 51             try (bytearrayinputstream stream = new bytearrayinputstream(content.getvalue())) {
 52                 // 拼接一个文件名做临时文件
 53                 string destfm = this.transactionfilename(this.transactionid, content.getkey(), path);
 54                 this.channelsftp.put(stream, destfm);
 55                 this.opfiles.add(destfm);
 56             }
 57         }
 58     }
 59 
 60     /**
 61      * 删除文件
 62      * @param contents 存放要删除的文件名
 63      * @param path 文件的绝对路径(不包含文件名)
 64      * @throws exception
 65      */
 66     public void delete(list<string> contents, string path) throws exception {
 67         if (this.optype == -1) {
 68             this.optype = 2;
 69         } else {
 70             throw new illegalstateexception();
 71         }
 72         for (string name : contents) {
 73             string destfm = this.transactionfilename(this.transactionid, name, path);
 74             this.channelsftp.rename(path+name, destfm);
 75             this.opfiles.add(destfm);
 76         }
 77     }
 78 
 79     /**
 80      * 提交事务
 81      * @param path 绝对路径(不包含文件名)
 82      * @throws exception
 83      */
 84     public void commit(string path) throws exception {
 85         switch (this.optype) {
 86             case 1:
 87                 for (string fm : this.opfiles) {
 88                     string destfm = this.untransactionfilename(fm, path);
 89                     //将之前的临时文件命名为真正需要的文件名
 90                     this.channelsftp.rename(fm, destfm);
 91                 }
 92                 break;
 93             case 2:
 94                 for (string fm : opfiles) {
 95                     //删除这个文件
 96                     this.channelsftp.rm(fm);
 97                 }
 98                 break;
 99             default:
100                 throw new illegalstateexception();
101         }
102         this.channelsftp.disconnect();
103     }
104 
105     /**
106      * 回滚事务
107      * @param path 绝对路径(不包含文件名)
108      * @throws exception
109      */
110     public void rollback(string path) throws exception {
111         switch (this.optype) {
112             case 1:
113                 for (string fm : opfiles) {
114                     // 删除这个文件
115                     this.channelsftp.rm(fm);
116                 }
117                 break;
118             case 2:
119                 for (string fm : opfiles) {
120                     string destfm = this.untransactionfilename(fm, path);
121                     // 将文件回滚
122                     this.channelsftp.rename(fm, destfm);
123                 }
124                 break;
125             default:
126                 throw new illegalstateexception();
127         }
128         this.channelsftp.disconnect();
129     }
130 }

sftptransactionmanager:这个是对事务的操作。

 1 import org.springframework.beans.factory.annotation.autowired;
 2 import org.springframework.stereotype.component;
 3 
 4 @component
 5 public class sftptransactionmanager {
 6     @autowired
 7     private sftpclient client;
 8 
 9     //开启事务
10     public sftptransaction starttransaction() throws exception {
11         return new sftptransaction(client);
12     }
13 
14     /**
15      * 提交事务
16      * @param transaction
17      * @param path 绝对路径(不包含文件名)
18      * @throws exception
19      */
20     public void committransaction(sftptransaction transaction, string path) throws exception {
21         transaction.commit(path);
22     }
23 
24     /**
25      * 回滚事务
26      * @param transaction
27      * @param path 绝对路径(不包含文件名)
28      * @throws exception
29      */
30     public void rollbacktransaction(sftptransaction transaction, string path) throws exception {
31         transaction.rollback(path);
32     }
33 }

sftptransactiontest:这是一个测试类,使用之前可以先行测试是否可行,有问题可以评论

 1 import com.springcloud.utils.sftputil.sftptransaction;
 2 import com.springcloud.utils.sftputil.sftptransactionmanager;
 3 import org.apache.commons.lang3.tuple.immutablepair;
 4 import org.apache.commons.lang3.tuple.pair;
 5 import org.junit.test;
 6 
 7 import java.util.arraylist;
 8 import java.util.list;
 9 
10 /**
11  *  测试文件事务管理
12  */
13 public class sftptransactiontest {
14 
15     //创建文件
16     @test
17     public static void createfile() throws exception {
18         // 定义一个存放文件的绝对路径
19         string targetpath = "/data/file/";
20         //创建一个事务管理实例
21         sftptransactionmanager manager = new sftptransactionmanager();
22         sftptransaction sftptransaction = null;
23         try {
24             //开启事务并返回一个事务实例
25             sftptransaction = manager.starttransaction();
26             //创建一个存放要操作文件的集合
27             list<pair<string, byte[]>> contents = new arraylist<>();
28             immutablepair apair = new immutablepair<>("file_a", "data_a".getbytes());  //file_a是文件a的名字,data_a是文件a的内容
29             immutablepair bpair = new immutablepair<>("file_b", "data_b".getbytes());
30             immutablepair cpair = new immutablepair<>("file_c", "data_c".getbytes());
31             contents.add(apair);
32             contents.add(bpair);
33             contents.add(cpair);
34             // 将内容进行事务管理
35             sftptransaction.create(contents, targetpath);
36             // 事务提交
37             manager.committransaction(sftptransaction, targetpath);
38         }catch (exception e) {
39             if (sftptransaction != null) {
40                 // 发生异常事务回滚
41                 manager.rollbacktransaction(sftptransaction, targetpath);
42             }
43             throw e;
44         }
45     }
46     
47     //删除文件
48     @test
49     public void deletefile() throws exception {
50         // 定义一个存放文件的绝对路径
51         string targetpath = "/data/file/";
52         //创建一个事务管理实例
53         sftptransactionmanager manager = new sftptransactionmanager();
54         sftptransaction sftptransaction = null;
55         try {
56             //开启事务并返回一个事务实例
57             sftptransaction = manager.starttransaction();
58             list<string> contents = new arraylist<>();
59             contents.add("file_a");  // file_a要删除的文件名
60             contents.add("file_b");
61             contents.add("file_c");
62             sftptransaction.delete(contents, targetpath);
63             manager.committransaction(sftptransaction, targetpath);
64         } catch (exception e) {
65             //回滚事务
66             if (sftptransaction != null) {
67                 manager.rollbacktransaction(sftptransaction, targetpath);
68             }
69             throw e;
70         }
71     }
72 }

这是对于sftp文件操作的依赖,其他的依赖应该都挺好。

 1 <dependency> 2 <groupid>com.jcraft</groupid> 3 <artifactid>jsch</artifactid> 4 </dependency> 

  ok,到这里已经完了,之前有需要写文件事务管理的时候只找到一个谷歌的包可以完成(包名一时半会忘记了),但是与实际功能还有些差别,所以就根据那个源码自己改了改,代码写的可能很一般,主要也是怕以后自己用忘记,就记下来,如果刚好能帮到有需要的人,那就更好。哪位大神如果有更好的方法也请不要吝啬,传授一下。(抱拳)

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网