使用二維碼實現文件分享

有時候,想要把電腦上的文件快速便捷的傳輸到手機,就可以用到這個工具了。

使用方法示例:

    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

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