有时候,想要把电脑上的文件快速便捷的传输到手机,就可以用到这个工具了。
使用方法示例:
1、准备需要传输到手机的文件
注:1.2版本不仅支持对文件的一键分享,还支持文件夹的一键分享。
2、选中文件或文件夹,右键属性,点击QrShare
3、弹出二维窗口,用手机扫描二维码下载文件
提示:此时鼠标的粘贴板已经粘贴了二维码的内容,直接发给小伙伴,分析文件吧。
4、手机下载文件结果
下载时,服务端可以强制断开分享,也可以等待客户端下载完成后自动关闭分享;同时,客户端同时在线下载数量不能超过10个。
当分享的文件类型是图片格式(png,jpg,gif,bmp,ico等)和网页格式(html,htm)时,浏览器会对其数据进行页面显示。即:直接显示图片而不是弹出下载图片(可通过另存为下载图片);直接显示html内容而不是提示下载文件。其他格式文件,直接提示下载文件。
注意:需要安装jdk1.6+,手机和电脑必须在同一局域网内,除非电脑部署在外网环境。
程序分享:
先说说使用的技术:
代码如下:
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 hints = new Hashtable();
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 accomplishMap = new Hashtable();// 防止未下载完成,关闭服务
/**
* 开启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
version 4.0
中间版本直接忽略吧,最新版本4.0.0 ~
最新工具下载地址: https://download.csdn.net/download/qq_25166683/12384359(4.0.0版本)
IT视频教程集合:http://blog.sina.com.cn/s/blog_189450fd80102xp2f.html
原文地址:https://blog.csdn.net/qq_25166683/article/details/81281177