/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.ftp;

import com.crunii.micro.common.utils.*;
import com.jcraft.jsch.ChannelSftp;
import org.apache.commons.io.IOUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <pre>
 *  频繁调用sftp的上传下载，如果传输大文件请使用SFTPUtil
 *  注意连接池的最大连接数不要配置太大，有可能linux主机有限制
 *  一般配置20个足以
 * </pre>
 *
 * @author 田平 create 2019年3月28日下午3:27:57
 */
public class SftpRemoteFile implements RemoteFile {

	private GenericObjectPool<ChannelSftp> pool;

	public SftpRemoteFile(GenericObjectPool<ChannelSftp> pool) {
		this.pool = pool;
	}

	public static void main(String[] args) throws Exception {

		ChannelSftpFactory factory = new ChannelSftpFactory();
		factory.setHost("192.168.10.142");
		factory.setUsername("microsftp");
		factory.setPassword("@Crunii,.123");
		factory.setPort(22);
		factory.afterPropertiesSet();
		ChannelSftpPoolFactory poolFactory = new ChannelSftpPoolFactory();
		poolFactory.setChannelSftpFactory(factory);
		poolFactory.afterPropertiesSet();
		poolFactory.getConfig().setMaxTotal(20);
		String localFilePath = "C:\\Users\\Administrator\\Desktop\\json.txt";
		String remotePath = "/home/microsftp/";
		SftpRemoteFile sshTool = new SftpRemoteFile(poolFactory.getObject());

		int threadSize = 1000;
		ExecutorService es = Executors.newFixedThreadPool(100);
		CountDownLatch doneSignal = new CountDownLatch(threadSize);
		Date date = new Date();
		for (int i = 0; i < threadSize; i++) {
			es.execute(new Runnable() {
				@Override
				public void run() {
					sshTool.uploadFile(remotePath, NanoIdUtil.randomLowerNanoId() + ".txt", FileUtil.getFileInputStream(localFilePath));
					doneSignal.countDown();
				}
			});
		}
		doneSignal.await();
		System.out.println("------>used " + DateUtil.getUseTime(date) + "ms");

		sshTool.uploadFile(remotePath, "a.txt", FileUtil.getFileInputStream(localFilePath));
		sshTool.downloadFile(remotePath, "a.txt", "d://", "b.txt");
		String content = new String(sshTool.getFileBytes(remotePath, "a.txt"));
		System.out.println(content);
		factory.destroy();
		poolFactory.destroy();
		StopUtil.stopES(es);

	}

	/**
	 * <pre>
	 * 上传本地文件到远程目录，远程目录不存在自动创建
	 * </pre>
	 *
	 * @param remotePath
	 * @param remoteName
	 * @param localFilePath
	 */
	@Override
	public void uploadFile(String remotePath, String remoteName, String localFilePath) {
		uploadFile(remotePath, remoteName, FileUtil.getFileInputStream(localFilePath));
	}

	/**
	 * <pre>
	 * 上传本地文件流到远程目录，远程目录不存在自动创建
	 * </pre>
	 *
	 * @param remotePath
	 * @param remoteName
	 * @param in
	 */
	@Override
	public void uploadFile(String remotePath, String remoteName, InputStream in) {
		ChannelSftp channelSftp = null;
		String realPath = FileUtil.getRealPath(remotePath);
		try {
			channelSftp = pool.borrowObject();
			SftpUtil.mkdirWithP(channelSftp, realPath);
			channelSftp.put(in, realPath + remoteName);
		} catch (Exception e) {
			throw new RuntimeException("uploadFile error!", e);
		} finally {
			IOUtils.closeQuietly(in);
			pool.returnObject(channelSftp);
		}
	}

	/**
	 * <pre>
	 * 下载远程文件的字节流
	 * </pre>
	 *
	 * @param remotePath
	 * @param remoteName
	 * @return
	 */
	@Override
	public byte[] getFileBytes(String remotePath, String remoteName) {
		String realPath = FileUtil.getRealPath(remotePath);
		ChannelSftp channelSftp = null;
		byte[] bytes = null;
		InputStream input = null;
		try {
			channelSftp = pool.borrowObject();
			input = channelSftp.get(realPath + remoteName);
			bytes = FileCopyUtils.copyToByteArray(input);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			pool.returnObject(channelSftp);
		}
		return bytes;
	}

	@Override
	public int copyRange(String remotePath, String remoteName, int start, int end, OutputStream output) {
		String realPath = FileUtil.getRealPath(remotePath);
		ChannelSftp channelSftp = null;
		InputStream input = null;
		try {
			channelSftp = pool.borrowObject();
			input = channelSftp.get(realPath + remoteName);
			Assert.notNull(input, "No InputStream specified");
			Assert.notNull(output, "No OutputStream specified");
			return (int) StreamUtils.copyRange(input, output, start, end);
		} catch (Exception e) {
			throw new RuntimeException("copyRange error!", e);
		} finally {
			IOUtils.closeQuietly(input);
			IOUtils.closeQuietly(output);
			pool.returnObject(channelSftp);
		}
	}

	@Override
	public int copyToOutputStream(String remotePath, String remoteName, OutputStream output) {
		String realPath = FileUtil.getRealPath(remotePath);
		ChannelSftp channelSftp = null;
		InputStream input = null;
		try {
			channelSftp = pool.borrowObject();
			input = channelSftp.get(realPath + remoteName);
			Assert.notNull(input, "No InputStream specified");
			Assert.notNull(output, "No OutputStream specified");
			//自动关闭流
			return FileCopyUtils.copy(input, output);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			pool.returnObject(channelSftp);
		}
	}

	/**
	 * <pre>
	 * 下载远程文件到本地
	 * </pre>
	 *
	 * @param remotePath
	 * @param remoteName
	 * @param localFilePath
	 * @param localFileName
	 */
	@Override
	public void downloadFile(String remotePath, String remoteName, String localFilePath, String localFileName) {
		String realPath = FileUtil.getRealPath(remotePath);
		String realLocalPath = FileUtil.getRealPath(localFilePath);
		ChannelSftp channelSftp = null;
		InputStream input = null;
		OutputStream output = null;
		try {
			channelSftp = pool.borrowObject();
			input = channelSftp.get(realPath + remoteName);
			File file = new File(realLocalPath + localFileName);
			output = new FileOutputStream(file);
			FileCopyUtils.copy(input, output);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			pool.returnObject(channelSftp);
		}
	}

	/**
	 * @return the pool
	 */
	public GenericObjectPool<ChannelSftp> getPool() {
		return pool;
	}

	/**
	 * @param pool the pool to set
	 */
	public void setPool(GenericObjectPool<ChannelSftp> pool) {
		this.pool = pool;
	}

}
