使用二维码实现文件分享

有时候,想要把电脑上的文件快速便捷的传输到手机,就可以用到这个工具了。

使用方法示例:

    1、准备需要传输到手机的文件

    

    :1.2版本不仅支持对文件的一键分享,还支持文件夹的一键分享。

    2、选中文件或文件夹,右键属性,点击QrShare

    

   3、弹出二维窗口,用手机扫描二维码下载文件

   

   提示:此时鼠标的粘贴板已经粘贴了二维码的内容,直接发给小伙伴,分析文件吧。

   4、手机下载文件结果

    

    下载时,服务端可以强制断开分享,也可以等待客户端下载完成后自动关闭分享;同时,客户端同时在线下载数量不能超过10个。

     当分享的文件类型是图片格式(png,jpg,gif,bmp,ico等)和网页格式(html,htm)时,浏览器会对其数据进行页面显示。即:直接显示图片而不是弹出下载图片(可通过另存为下载图片);直接显示html内容而不是提示下载文件。其他格式文件,直接提示下载文件。

 直接显示出图片。

注意:需要安装jdk1.6+,手机和电脑必须在同一局域网内,除非电脑部署在外网环境。

 

程序分享:

先说说使用的技术:

  1. 使用zxing生成二维码
  2. jdk swing的基础知识
  3. jdk httpServer
  4. jdk zip压缩文件
  5. 如何将可jar转换成可执行的exe文件
  6. 修改windos注册表,实现右键菜单

代码如下:

QrShare.java

package com.kancy.httpserver;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

/**
 * 使用二维码实现文件分享
 * @author kancy
 * @see 2018/07/30
 */
public class QrShare {
	
	public static void main(String[] arg) {

		try {
			// 1.获取文件路径,获取失败时,系统自动关闭
			File file = acquireAndCheckDownloadFile(arg);

			// 2.启动服务
			startJavaHttpFileServer(file);

			// 3.显示二维码
			String title = "  扫码下载文件("+ file.getName() +")";
			String content = "http://" + InetAddress.getLocalHost().getHostAddress().toString() + ":" + HTTP_PORT
					+ "/download"+ getFileSuffix(file) + "?id=" + System.currentTimeMillis();
			setSysClipboardText(content);
			createQrImageFrame(title, content);
			waitDelay(Setting.getProperty("wait_delay_time", Setting.wait_delay_time));
			
			// 4.提示是否强制关闭服务
			int unAccomplishedSize = accomplishMap.size();
			if(unAccomplishedSize > 0){
				int result = JOptionPane.showConfirmDialog(null, "目前存在"+ unAccomplishedSize+"个客户端正在下载,是否强制关闭服务?","系统关闭提示",2);
				if(result == 0){
					closeService();
					System.exit(0);
				}
			}
			
			// 5.系统退出
			waitAndExitSystem(0);
			
		} catch (Exception e) {
			alertAndExit("系统异常:" + e.getMessage());
		}
	}


	/**
	 * 检查和获取文件
	 * @param arg
	 * @return
	 */
	private static File acquireAndCheckDownloadFile(String[] arg) {
		if (arg == null || arg.length <= 0) {
			alertAndExit("请指定下载的文件全路径!");
		}
		
		// 获取本地文件
		String filePath = arg[0];
		File file = new File(filePath);
		
		if(!file.exists()){
			alertAndExit("文件不存在!");
		}
		
		// 如果是目录,先进行zip打包
		if(file.isDirectory()){
			filePath = packZipFile(file);
			file = new File(filePath);
		}
		
		if (!file.isFile()) {
			alertAndExit("文件不存在!");
		}
		return file;
	}

	/**
	 * 压缩目录
	 * @param dir
	 * @return
	 */
	private static String packZipFile(File dir) {
		String zipFilePath = dir.getParent().replace("\\", "/") + "/" + dir.getName() + ".zip";
		try {
			ZipUtils.zip(zipFilePath, dir);
		} catch (Exception e) {
			e.printStackTrace();
			alertAndExit("压缩目录异常:\n"+e.getMessage());
		}
		return zipFilePath;
		
	}

	/**
	 * 使用随机端口
	 * @return
	 */
	private static int getRandomPort() {
		Random random = new Random();
		String value = "";
		while(true){
			int num = random.nextInt(10);
			// 确保端口小于65535
			if(value.length() == 0 && (num == 0 || num > 5)){
				continue;
			}
			value += num;
			if(value.length() == 5){
				break;
			}
		}
		return Integer.parseInt(value);
	}

	/**
	 * 提示并且退出
	 * @param msg
	 */
	private static void alertAndExit(String msg) {
		JOptionPane.showMessageDialog(null, msg);
		System.exit(0);
	}
	
	/**
	 * 关闭服务
	 */
	private static void closeService() {
		if(!threadPool.isShutdown()){
			threadPool.shutdown();
		}
		server.stop(0);
	}
	
	/**
	 * 关闭系统
	 * @param i
	 * @throws InterruptedException
	 */
	private static void waitAndExitSystem(int i) {
		int unAccomplishedSize = accomplishMap.size();
		if(unAccomplishedSize == 0){
			closeService();
			System.out.println("系统即将关闭!");
			System.exit(0);
		}else{
			System.out.println("存在"+unAccomplishedSize+"个活动链接,请等待...");
			waitDelay(1000);
			waitAndExitSystem(i);
		}
	}
	
	/**
	 * 睡眠
	 * @param l
	 */
	private static void waitDelay(long l) {
		try {
			Thread.sleep(l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将字符串复制到剪切板。
	 */
	private static void setSysClipboardText(String writeMe) {
		Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
		Transferable tText = new StringSelection(writeMe);
		clip.setContents(tText, null);
	}

	/**
	 * 依赖 com.google.zxing core-3.0.0.jar
	 * 
	 * @param title
	 * @param content
	 * @throws WriterException
	 */
	private static void createQrImageFrame(String title, String content) throws WriterException {
		Map<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
		hints.put(EncodeHintType.CHARACTER_SET, Setting.default_charset);
		BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
		int width = matrix.getWidth();
		int height = matrix.getHeight();
		BufferedImage image = new BufferedImage(width, height, 1);
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				image.setRGB(x, y, matrix.get(x, y) ? -16777216 : -1);
			}
		}
		ImageIcon icon = new ImageIcon(image);
		JOptionPane.showMessageDialog(null, "", title, 1, icon);
	}
	
	//-----------------------------------------------------------------------------
	// jdk HttpServer 提供http服务
	//-----------------------------------------------------------------------------

	private static final int MAX_POOL_NUM = Setting.http_pool_size > 0 ? Setting.http_pool_size : 10;
	
	private static final int HTTP_PORT = Setting.http_port > 0 ? Setting.http_port : getRandomPort();
	
	private static HttpServer server;
	
	private static ExecutorService threadPool = Executors.newFixedThreadPool(MAX_POOL_NUM);// 只允许10个用户同时下载
	
	private static Map<String, Boolean> accomplishMap = new Hashtable<String, Boolean>();// 防止未下载完成,关闭服务
	
	/**
	 * 开启http服务
	 * @param file
	 * @throws Exception
	 */
	private static void startJavaHttpFileServer(File file) throws Exception {
		server = HttpServer.create(new InetSocketAddress(HTTP_PORT), 0);
		server.setExecutor(threadPool);
		server.createContext("/download"+ getFileSuffix(file), new FileServerHandler(file));
		Thread httpServiceThread = new Thread(new Runnable() {
			@Override
			public void run() {
				server.start();
				System.out.println("服务启动成功!");
			}
		});
		httpServiceThread.start();
	}
	
	/**
	 * 获取文件后缀
	 * @param file
	 * @return
	 */
	private static String getFileSuffix(File file) {
		String name = file.getName();
		String fileSuffix = "";
		if(name.contains(".")){
			fileSuffix = name.substring(name.lastIndexOf("."), name.length());
		}
		return fileSuffix.trim().toLowerCase();
	}

	/**
	 * 文件下载静态处理器
	 * @author kancy
	 */
	static class FileServerHandler implements HttpHandler {
		private File file;

		public FileServerHandler(File file) {
			this.file = file;
		}
		@Override
		public void handle(HttpExchange exchange) throws IOException {
			
			if(accomplishMap.size() >= MAX_POOL_NUM){
				String msg = "服务队列已满,请稍候下载!";
				exchange.sendResponseHeaders(200, 0);
				exchange.getResponseBody().write(msg.getBytes(Setting.default_charset));
				exchange.getResponseBody().flush();
				exchange.getResponseBody().close();
				return;
			}
			
			String handlerId = UUID.randomUUID().toString();
			
			try {
				
				// 开始处理
				accomplishMap.put(handlerId, false);
				
				OutputStream os = exchange.getResponseBody();
				FileInputStream in = new FileInputStream(file);
				
				// 判断文件的类型
				// 1.图片类型/网页类型 直接显示在浏览器,通过另存为下载
				// 3.其他类型 下载模式
				String fileSuffix = getFileSuffix(file);
				String contentType = Constants.getContentType(fileSuffix);
				
				// 设置下载头
				if(Constants.CONTENT_TYPE_DEFAULT.equals(contentType)
						|| (!Setting.show_static_resource && !Constants.CONTENT_TYPE_DEFAULT.equals(contentType) )){
					exchange.getResponseHeaders().add("Content-Disposition", "attachment;filename=\"" 
							+ new String(file.getName().getBytes(Setting.default_charset), Setting.content_disposition_charset) + "\"");
				}
				exchange.getResponseHeaders().add("Content-Type", contentType);
				exchange.getResponseHeaders().add("Content-Length", String.valueOf(file.length()));

				// 发送响应
				exchange.sendResponseHeaders(200, file.length());

				// 写文件
				writeAndClose(os, in);
				
			} finally {
				// 处理完成
				accomplishMap.remove(handlerId);
			}
		}
		
		/**
		 * 写文件并且关闭流
		 * @param os
		 * @param in
		 * @throws IOException
		 */
		private static void writeAndClose(OutputStream os, FileInputStream in) throws IOException {
			try {
				byte[] buf = new byte[1024];
				int size = -1;
				while ((size = in.read(buf)) != -1) {
					os.write(buf, 0, size);
					os.flush();
				}
			} finally{
				if(os != null){
					os.close();
				}
				if(in != null){
					in.close();
				}
			}
		}
	}
}

ZipUtils.java

package com.kancy.httpserver;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * ZIP 工具
 * 
 * @author kancy
 * @see 2018/07/30
 */
public class ZipUtils {

	public static void unzip(String srcZip, String outPath) {
		long startTime = System.currentTimeMillis();
		try {
			ZipInputStream Zin = new ZipInputStream(new FileInputStream(srcZip));// 输入源zip路径
			BufferedInputStream Bin = new BufferedInputStream(Zin);
			File Fout = null;
			ZipEntry entry;
			try {
				while ((entry = Zin.getNextEntry()) != null && !entry.isDirectory()) {
					Fout = new File(outPath, entry.getName());
					if (!Fout.exists()) {
						(new File(Fout.getParent())).mkdirs();
					}
					FileOutputStream out = new FileOutputStream(Fout);
					BufferedOutputStream Bout = new BufferedOutputStream(out);
					int b;
					while ((b = Bin.read()) != -1) {
						Bout.write(b);
					}
					Bout.close();
					out.close();
					System.out.println(Fout + "解压成功");
				}
				Bin.close();
				Zin.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		long endTime = System.currentTimeMillis();
		System.out.println("耗费时间: " + (endTime - startTime) + " ms");
	}

	public static void zip(String zipFileName, File inputFile) throws Exception {
		System.out.println("压缩中...");
		File file = new File(zipFileName);
		if (!file.exists())
			file.createNewFile();
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
		BufferedOutputStream bo = new BufferedOutputStream(out);
		zip(out, inputFile, inputFile.getName(), bo);
		bo.close();
		out.close(); // 输出流关闭
		System.out.println("压缩完成");
	}

	public static void zip(String zipFileName, String... filePaths) throws Exception {
		System.out.println("压缩中...");
		File file = new File(zipFileName);
		if (!file.exists())
			file.createNewFile();
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
		BufferedOutputStream bo = new BufferedOutputStream(out);
		for (int i = 0; i < filePaths.length; i++) {
			File inputFile = new File(filePaths[i]);
			zip(out, inputFile, inputFile.getName(), bo);
		}
		bo.close();
		out.close();
		System.out.println("压缩完成");
	}

	private static void zip(ZipOutputStream out, File f, String base, BufferedOutputStream bo) throws Exception { // 方法重载
		if (f.isDirectory()) {
			File[] fl = f.listFiles();
			if (fl.length == 0) {
				out.putNextEntry(new ZipEntry(base + "/")); // 创建zip压缩进入点base
				System.out.println(base + "/");
			}
			for (int i = 0; i < fl.length; i++) {
				zip(out, fl[i], base + "/" + fl[i].getName(), bo); // 递归遍历子文件夹
			}
		} else {
			out.putNextEntry(new ZipEntry(base)); // 创建zip压缩进入点base
			System.out.println(base);
			FileInputStream in = new FileInputStream(f);
			BufferedInputStream bi = new BufferedInputStream(in);
			int b;
			while ((b = bi.read()) != -1) {
				bo.write(b); // 将字节流写入当前zip目录
			}
			bi.close();
			in.close(); // 输入流关闭
		}
	}

}

Setting.java

package com.kancy.httpserver;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;

/**
 * 系统设置
 * @author kancy
 *
 */
public class Setting {
	
	/**
	 * 是否显示图片和网页资源
	 */
	public static boolean show_static_resource = true;
	/**
	 * 采用固定的端口设置
	 */
	public static int http_port = 0;
	/**
	 * 线程池大小设置,同时下载最大数量控制
	 */
	public static int http_pool_size = 0;
	/**
	 * 延迟自动关闭系统,时间单位毫秒
	 */
	public static long wait_delay_time = 1L;
	/**
	 * 默认编码
	 */
	public static String default_charset = Constants.CHARACTER_UTF8;
	/**
	 * content_disposition编码
	 */
	public static String content_disposition_charset = Constants.CHARACTER_ISO8859_1;
	
	//----------------------------------------------------------------------------
	// setting end
	//-----------------------------------------------------------------------------
	
	private static String configpath = System.getProperty("user.home").replace("\\", "/").concat("/").concat("QrShare/config.ini");
	
	private volatile static Properties setting = new Properties();
	
	public static String getProperty(String key) {
		return setting.getProperty(key);
	}
	public static String getProperty(String key, String defaultValue) {
		return setting.getProperty(key, defaultValue);
	}
	public static int getProperty(String key, int defaultValue) {
		return Integer.parseInt(setting.getProperty(key, String.valueOf(defaultValue)));
	}
	public static long getProperty(String key, long defaultValue) {
		return Long.parseLong(setting.getProperty(key, String.valueOf(defaultValue)));
	}
	public static boolean getProperty(String key, boolean defaultValue) {
		return "true".equalsIgnoreCase(setting.getProperty(key, String.valueOf(defaultValue)));
	}
	public static void setProperty(String key, String value) {
		setting.setProperty(key, value);
		storeSetting();
	}
	
	private static void storeSetting() {
		OutputStream fos = null;
		try {
			fos = new FileOutputStream(configpath);// 加载读取文件流
			setting.store(fos, "QrShare Config");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 初始化设置
	 */
	static{
		init();
		// 取值
		show_static_resource = getProperty("show_static_resource", show_static_resource);
		wait_delay_time = getProperty("wait_delay_time", wait_delay_time);
		http_port = getProperty("http_port", http_port);
		http_pool_size = getProperty("http_pool_size", http_pool_size);
		default_charset = getProperty("default_charset", default_charset);
		content_disposition_charset = getProperty("content_disposition_charset", content_disposition_charset);
	}
	
	private static void initDefaultConfig() {
		setting.setProperty("default_charset", default_charset);
		setting.setProperty("wait_delay_time", String.valueOf(wait_delay_time));
		setting.setProperty("http_port", String.valueOf(http_port));
		setting.setProperty("http_pool_size", String.valueOf(http_pool_size));
		setting.setProperty("show_static_resource", String.valueOf(show_static_resource));
		setting.setProperty("content_disposition_charset", content_disposition_charset);
		storeSetting();
	}
	
	private static void init() {
		FileInputStream fis = null;
		try {
			File file = new File(configpath);
			if(!file.getParentFile().exists() && !file.getParentFile().isDirectory()){
				file.getParentFile().mkdirs();
			}
			if (!(file.exists() && file.isFile())) {
				file.createNewFile();
				initDefaultConfig();
			}
			file.setReadable(true);
			file.setWritable(true);
			fis = new FileInputStream(configpath);
			setting.load(fis);
		} catch (Exception e) {
			e.printStackTrace();
		}  finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
}

Constants.java

package com.kancy.httpserver;

import java.util.Properties;

/**
 * 常量
 * @author kancy
 *
 */
public class Constants {
	public static final String CHARACTER_UTF8 = "UTF-8";
	public static final String CHARACTER_GBK = "GBK";
	public static final String CHARACTER_ISO8859_1 = "ISO8859-1";
	//---------------------------------------------------------
	// http://tool.oschina.net/commons
	//---------------------------------------------------------
	public static final String CONTENT_TYPE_IMAGE = "image/jpeg";
	public static final String CONTENT_TYPE_GIF = "image/gif";
	public static final String CONTENT_TYPE_PNG = "image/png";
	public static final String CONTENT_TYPE_ICO= "image/x-icon";
	public static final String CONTENT_TYPE_JPG = "image/jpeg";
	public static final String CONTENT_TYPE_JPEG = "image/jpeg";
	public static final String CONTENT_TYPE_BMP = "image/jpeg";
	public static final String CONTENT_TYPE_HTML = "text/html";
	public static final String CONTENT_TYPE_DEFAULT = "application/octet-stream";
	
	private static Properties staticResourceContentTypes = new Properties();
	static{
		staticResourceContentTypes.setProperty(".html", CONTENT_TYPE_HTML);
		staticResourceContentTypes.setProperty(".htm", CONTENT_TYPE_HTML);
		staticResourceContentTypes.setProperty(".gif", CONTENT_TYPE_GIF);
		staticResourceContentTypes.setProperty(".png", CONTENT_TYPE_PNG);
		staticResourceContentTypes.setProperty(".ico", CONTENT_TYPE_ICO);
		staticResourceContentTypes.setProperty(".jpeg", CONTENT_TYPE_JPG);
		staticResourceContentTypes.setProperty(".jpg", CONTENT_TYPE_JPEG);
		staticResourceContentTypes.setProperty(".bmp", CONTENT_TYPE_BMP);
	}
	
	public static String getContentType(String fileSuffix){
		assert fileSuffix != null;
		return staticResourceContentTypes.getProperty(fileSuffix.trim().toLowerCase(), CONTENT_TYPE_DEFAULT);
	}
	
}

 

将jar转换成exe文件:

   这里不做教程,有兴趣可以去了解 exe4j :http://www.softpedia.com/get/Authoring-tools/Setup-creators/exe4j.shtml

 

添加右键菜单:

  安装:

      reg add "HKEY_CLASSES_ROOT\*\shell\QrShare\command" /ve /d "\"%~dp0\QrShare.exe\" \"%%1\"" /f

      reg add "HKEY_CLASSES_ROOT\Directory\shell\QrShare\command" /ve /d "\"%~dp0\QrShare.exe\" \"%%1\"" /f

  卸载:

      reg delete "HKEY_CLASSES_ROOT\*\shell\QrShare" /f

      reg delete "HKEY_CLASSES_ROOT\Directory\shell\QrShare" /f

 

无法运行说明:

如果遇到以下错误,请更换32版本的QrShare.exe,直接覆盖,名称改为QrShare.exe

免费下载链接:https://pan.baidu.com/s/1hdVmGj1rVrOkq7zdUUxDWg 密码:2uxa

 

修改记录:

version1.1

1.文件路径带有空格问题修复

下载地址:https://download.csdn.net/download/qq_25166683/10572340

version1.2

1.添加文件夹分享

 下载地址:https://download.csdn.net/download/qq_25166683/10572845

version1.3

1.添加设置功能
2.图片和网页可以直接显示,而不是下载,但可以通过另存为下载
3.微信可以支持直接下载和图片的查看
4.部分优化 (其中:软件包体积减半,要的就是轻便小)
 

最新工具下载地址: https://download.csdn.net/download/qq_25166683/10574889(1.3版本)

IT视频教程集合:http://blog.sina.com.cn/s/blog_189450fd80102xp2f.html

原文地址:https://blog.csdn.net/qq_25166683/article/details/81281177

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章