使用原生Java代码生成可执行Jar包
最近想做一个功能,就是把我们编译后的字节码及其资源文件打包成一个可执行的jar包,在装有jre的机器上双击就能运行。
首先是我们需要选择哪些字节码和文件需要打包到文件中,这个我们用JFileChooser来做,让用户选择,我做了一个窗体来让用户选择。
效果如下:

我们让浏览文件系统,并选择需要打包的文件夹,然后计算出可以作为启动类的文件,通过下方的下拉让用户选择。
生成文件路径在确认按钮点击后弹出文件保存框让用户选择就好(也可以弹出输入框)。
代码如下:
Main
package org.coderecord.commons.ejarmaker; import java.awt.EventQueue; import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException; public class Main { public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
EventQueue.invokeLater(new Runnable() { @Override
public void run() {
new FrmMain().setVisible(true);
}
});
} }
Main
FrmMain(只是界面代码,业务代码最后贴出)
package org.coderecord.commons.ejarmaker; import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List; import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.filechooser.FileFilter; public class FrmMain extends JFrame implements ActionListener { private static final long serialVersionUID = 2016913328739206536L;
// 选择的文件(用户在文件选择器中选择的)
private List<File> userSelectedFiles = new ArrayList<>();
// 我们经过分析得到的最终会被打包的文件
private List<File> finalFiles = new ArrayList<>(); public FrmMain() {
setSize(480, 320);
setResizable(false);
setLocationRelativeTo(null);
setTitle("通用可执行Jar包生成工具");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
// 在运行时获取资源文件的方式,一定是使用Class.getResource方式
// 在jar包中这种方式也行得通
// ‘/’代表根路径
setIconImage(Toolkit.getDefaultToolkit().getImage(FrmMain.class.getResource("/resources/icon.png")));
initComponents();
} // 初始化组件
private void initComponents() {
// 提示
lblTip = new JLabel("选择需要打包的文件并设置启动类");
lblTip.setLocation(20, 10);
lblTip.setSize(350, 20);
add(lblTip); // 浏览按钮
btnBrowser = new JButton("浏 览");
btnBrowser.setLocation(380, 10);
btnBrowser.setSize(80, 24);
btnBrowser.addActionListener(this);
add(btnBrowser); // 展示已选择文件
JScrollPane jspFiles = new JScrollPane();
txtFiles = new JTextArea();
txtFiles.setEditable(false);
jspFiles.setSize(440, 160);
jspFiles.setLocation(20, 40);
txtFiles.setSize(440, 201600);
txtFiles.setLocation(20, 40);
txtFiles.setFocusable(false);
jspFiles.setViewportView(txtFiles);
add(jspFiles); // 选择启动类
cobMainClass = new JComboBox<>();
cobMainClass.setSize(440, 30);
cobMainClass.setLocation(20, 210);
add(cobMainClass); // 清除已选
btnCls = new JButton("重 选");
btnCls.setLocation(20, 250);
btnCls.setSize(80, 24);
btnCls.addActionListener(this);
add(btnCls); // 确认按钮
btnConfirm = new JButton("确认");
btnConfirm.setSize(80, 24);
btnConfirm.setLocation(380, 250);
btnConfirm.addActionListener(this);
add(btnConfirm); // 文件选择器
jfcSelect = new JFileChooser();
// 可以选择文件和文件夹
jfcSelect.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
// 可以多选
jfcSelect.setMultiSelectionEnabled(true); // 文件保存
jfcSave = new JFileChooser();
// 设置只接受以“.jar”结尾的文件
jfcSave.setAcceptAllFileFilterUsed(false);
jfcSave.setFileFilter(new FileFilter() { @Override
public String getDescription() {
return "可执行Jar";
} @Override
public boolean accept(File f) {
return f.getName().endsWith(".jar");
}
});
} @Override
public void actionPerformed(ActionEvent e) { } private JLabel lblTip;
private JButton btnBrowser;
private JFileChooser jfcSelect;
private JTextArea txtFiles;
private JComboBox<String> cobMainClass;
private JButton btnCls;
private JButton btnConfirm;
private JFileChooser jfcSave;
}
FrmMain_UI
然后开始业务部分,首先是选择文件,我们允许用户选择多个文件和文件夹(甚至可以通过多次选择来选择不同盘符、路径下的文件和文件夹),在选择后可能有重复的地方或两次选择后有包含的项目,我们要去除。
我们为“浏览”按钮事件添加处理,让用户选择文件并处理选中文件:
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnBrowser) {
// 浏览
int result = jfcSelect.showOpenDialog(this); // 选择了文件
if(result == JFileChooser.APPROVE_OPTION) {
for(File file : jfcSelect.getSelectedFiles())
userSelectedFiles.add(file); // 整理选择的文件,去除重复项
removeDuplicateItems(userSelectedFiles); // 重新计算选中文件
finalFiles.clear();
for(File file : userSelectedFiles)
addFileToList(file, finalFiles); // 计算文件展示打包路径及展示路径
// 计算可启动类路径
// 展示到文本框中
cobMainClass.removeAllItems();
txtFiles.setText("");
File file,direc;
String filePath,direcPath;
Iterator<File> itd,itf;
for(itd = userSelectedFiles.iterator(); itd.hasNext();) {
direc = itd.next();
direcPath = direc.getAbsolutePath();
for(itf = finalFiles.iterator(); itf.hasNext();) {
file = itf.next();
filePath = file.getAbsolutePath();
if(filePath.equalsIgnoreCase(direcPath)) {
txtFiles.append(file.getName() + "\n");
filePaths.put(file.getName(), file);
//fileNames.put(file.getName(), file.getName().endsWith(".class")?file.getName().substring(0, file.getName().lastIndexOf('.')):file.getName());
if(file.getName().endsWith(".class"))
cobMainClass.addItem(file.getName().endsWith(".class")?file.getName().substring(0, file.getName().lastIndexOf('.')):file.getName());
itf.remove();
} else if(filePath.startsWith(direcPath)) {
String nameTmp = filePath.substring(direcPath.lastIndexOf(File.separator) + 1).replace(File.separatorChar, '/');
filePaths.put(nameTmp, file);
txtFiles.append(nameTmp + "\n");
//fileNames.put(nameTmp, nameTmp.endsWith(".class")?nameTmp.substring(0, nameTmp.lastIndexOf('.')).replace('/', '.'):nameTmp);
if(nameTmp.endsWith(".class") && nameTmp.indexOf('$') == -1)
cobMainClass.addItem(nameTmp.substring(0, nameTmp.lastIndexOf('.')).replace('/', '.'));
itf.remove();
}
}
}
}
}
} // 添加文件(非文件夹)到集合
private void addFileToList(File file, List<File> fileArr) {
if(file.isDirectory())
for(File child : file.listFiles())
addFileToList(child, fileArr);
else
fileArr.add(file);
} // 去除重复项
private void removeDuplicateItems(List<File> fileArr) {
// 去重复项
Set<String> directories = new HashSet<>();
Set<String> files = new HashSet<>();
for(File file : fileArr)
if(file.isDirectory())
directories.add(file.getAbsolutePath());
else
files.add(file.getAbsolutePath());
//去包含项(先去文件夹再去文件应该更好)
String fpath,dpath;
for(Iterator<String> itf = files.iterator(); itf.hasNext();) {
fpath = itf.next();
for(Iterator<String> itd = directories.iterator(); itd.hasNext();) {
dpath = itd.next();
if(fpath.startsWith(dpath))
itf.remove();
}
}
String dpath1,dpath2;
Set<String> directories1 = new HashSet<>(directories);
for(Iterator<String> itd1 = directories.iterator(); itd1.hasNext();) {
dpath1 = itd1.next();
for(Iterator<String> itd2 = directories1.iterator(); itd2.hasNext();) {
dpath2 = itd2.next();
if(dpath1.equals(dpath2))
continue;
else if(dpath2.startsWith(dpath1))
itd2.remove();
else if(dpath1.startsWith(dpath2))
itd1.remove();
}
}
directories.addAll(directories1); fileArr.clear();
for(String file : files)
fileArr.add(new File(file));
for(String directory : directories)
fileArr.add(new File(directory));
}
btnBrowser_event_handler
“重选”按钮点击后清除已选项,逻辑就先不详细介绍了。
然后是“确定”按钮,我们弹出文件保存框让用户选择保存位置,然后生成可执行的jar包:
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnBrowser) {
} else if(e.getSource() == btnCls) {
if(userSelectedFiles.size() == 0) return;
else if(JOptionPane.showConfirmDialog(this, "确定重选吗?将清除所有已选项!") == JOptionPane.OK_OPTION) {
userSelectedFiles.clear();
finalFiles.clear();
filePaths.clear();
cobMainClass.removeAllItems();
}
} else if(e.getSource() == btnConfirm) {
if(filePaths.size() == 0) {
JOptionPane.showMessageDialog(this, "未选择文件", "错误", JOptionPane.ERROR_MESSAGE);
return;
} else if(cobMainClass.getSelectedItem() == null) {
JOptionPane.showMessageDialog(this, "未选择启动类", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 打包
int result = jfcSave.showSaveDialog(this);
if(result == JFileChooser.APPROVE_OPTION) {
try {
// 清单文件
Manifest man = new Manifest();
// 版本和启动类路径必要
man.getMainAttributes().putValue(Name.MANIFEST_VERSION.toString(), "1.0");
man.getMainAttributes().putValue(Name.MAIN_CLASS.toString(), cobMainClass.getSelectedItem().toString());
// Class-Path一定不要,除非能保证将引用类(即import的类)都联合打包了
JarOutputStream jos = new JarOutputStream(new FileOutputStream(jfcSave.getSelectedFile()), man);
jos.setLevel(Deflater.BEST_COMPRESSION);
BufferedInputStream bis = null;
byte[] cache = new byte[1024];
StringBuffer config = new StringBuffer();
for(String name : filePaths.keySet()) {
bis = new BufferedInputStream(new FileInputStream(filePaths.get(name)), 1024);
config.append(name).append('=').append(bis.available()).append('\n');
jos.putNextEntry(new JarEntry(name));
int count;
while((count = bis.read(cache, 0, 1024)) != -1)
jos.write(cache, 0, count);
jos.closeEntry();
bis.close();
}
jos.flush();
jos.close();
JOptionPane.showMessageDialog(this, "导出成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
} catch(Exception ex) {
JOptionPane.showMessageDialog(this, ex.getMessage(), "异常", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
} }
btnConfirm_event_handler
当然,这里还有一个小问题:选择文件(自己写的文件名就算不加后缀也能保存成功-_-)。
先展示一下结果:

在文件系统中选择:

导出到桌面:


运行一下:

我最后再将完整的源码贴出一份:
package org.coderecord.commons.ejarmaker; import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;
import java.util.zip.Deflater; import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.filechooser.FileFilter; public class FrmMain extends JFrame implements ActionListener { private static final long serialVersionUID = 2016913328739206536L;
// 选择的文件(用户在文件选择器中选择的)
private List<File> userSelectedFiles = new ArrayList<>();
// 我们经过分析得到的最终会被打包的文件
private List<File> finalFiles = new ArrayList<>();
// 文件打包路径及物理文件
private Map<String, File> filePaths = new Hashtable<>(); public FrmMain() {
setSize(480, 320);
setResizable(false);
setLocationRelativeTo(null);
setTitle("通用可执行Jar包生成工具");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
// 在运行时获取资源文件的方式,一定是使用Class.getResource方式
// 在jar包中这种方式也行得通
// ‘/’代表根路径
setIconImage(Toolkit.getDefaultToolkit().getImage(FrmMain.class.getResource("/resources/icon.png")));
initComponents();
} // 初始化组件
private void initComponents() {
// 提示
lblTip = new JLabel("选择需要打包的文件并设置启动类");
lblTip.setLocation(20, 10);
lblTip.setSize(350, 20);
add(lblTip); // 浏览按钮
btnBrowser = new JButton("浏 览");
btnBrowser.setLocation(380, 10);
btnBrowser.setSize(80, 24);
btnBrowser.addActionListener(this);
add(btnBrowser); // 展示已选择文件
JScrollPane jspFiles = new JScrollPane();
txtFiles = new JTextArea();
txtFiles.setEditable(false);
jspFiles.setSize(440, 160);
jspFiles.setLocation(20, 40);
txtFiles.setSize(440, 201600);
txtFiles.setLocation(20, 40);
txtFiles.setFocusable(false);
jspFiles.setViewportView(txtFiles);
add(jspFiles); // 选择启动类
cobMainClass = new JComboBox<>();
cobMainClass.setSize(440, 30);
cobMainClass.setLocation(20, 210);
add(cobMainClass); // 清除已选
btnCls = new JButton("重 选");
btnCls.setLocation(20, 250);
btnCls.setSize(80, 24);
btnCls.addActionListener(this);
add(btnCls); // 确认按钮
btnConfirm = new JButton("确认");
btnConfirm.setSize(80, 24);
btnConfirm.setLocation(380, 250);
btnConfirm.addActionListener(this);
add(btnConfirm); // 文件选择器
jfcSelect = new JFileChooser();
// 可以选择文件和文件夹
jfcSelect.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
// 可以多选
jfcSelect.setMultiSelectionEnabled(true); // 文件保存
jfcSave = new JFileChooser();
// 设置只接受以“.jar”结尾的文件
jfcSave.setAcceptAllFileFilterUsed(false);
jfcSave.setFileFilter(new FileFilter() { @Override
public String getDescription() {
return "可执行Jar";
} @Override
public boolean accept(File f) {
return f.getName().endsWith(".jar");
}
});
} @Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnBrowser) {
// 浏览
int result = jfcSelect.showOpenDialog(this); // 选择了文件
if(result == JFileChooser.APPROVE_OPTION) {
for(File file : jfcSelect.getSelectedFiles())
userSelectedFiles.add(file); // 整理选择的文件,去除重复项
removeDuplicateItems(userSelectedFiles); // 重新计算选中文件
finalFiles.clear();
for(File file : userSelectedFiles)
addFileToList(file, finalFiles); // 计算文件展示打包路径及展示路径
// 计算可启动类路径
// 展示到文本框中
cobMainClass.removeAllItems();
txtFiles.setText("");
File file,direc;
String filePath,direcPath;
Iterator<File> itd,itf;
for(itd = userSelectedFiles.iterator(); itd.hasNext();) {
direc = itd.next();
direcPath = direc.getAbsolutePath();
for(itf = finalFiles.iterator(); itf.hasNext();) {
file = itf.next();
filePath = file.getAbsolutePath();
if(filePath.equalsIgnoreCase(direcPath)) {
txtFiles.append(file.getName() + "\n");
filePaths.put(file.getName(), file);
if(file.getName().endsWith(".class"))
cobMainClass.addItem(file.getName().endsWith(".class")?file.getName().substring(0, file.getName().lastIndexOf('.')):file.getName());
itf.remove();
} else if(filePath.startsWith(direcPath)) {
String nameTmp = filePath.substring(direcPath.lastIndexOf(File.separator) + 1).replace(File.separatorChar, '/');
filePaths.put(nameTmp, file);
txtFiles.append(nameTmp + "\n");
if(nameTmp.endsWith(".class") && nameTmp.indexOf('$') == -1)
cobMainClass.addItem(nameTmp.substring(0, nameTmp.lastIndexOf('.')).replace('/', '.'));
itf.remove();
}
}
}
}
} else if(e.getSource() == btnCls) {
if(userSelectedFiles.size() == 0) return;
else if(JOptionPane.showConfirmDialog(this, "确定重选吗?将清除所有已选项!") == JOptionPane.OK_OPTION) {
userSelectedFiles.clear();
finalFiles.clear();
filePaths.clear();
cobMainClass.removeAllItems();
}
} else if(e.getSource() == btnConfirm) {
if(filePaths.size() == 0) {
JOptionPane.showMessageDialog(this, "未选择文件", "错误", JOptionPane.ERROR_MESSAGE);
return;
} else if(cobMainClass.getSelectedItem() == null) {
JOptionPane.showMessageDialog(this, "未选择启动类", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 打包
int result = jfcSave.showSaveDialog(this);
if(result == JFileChooser.APPROVE_OPTION) {
try {
// 清单文件
Manifest man = new Manifest();
// 版本和启动类路径必要
man.getMainAttributes().putValue(Name.MANIFEST_VERSION.toString(), "1.0");
man.getMainAttributes().putValue(Name.MAIN_CLASS.toString(), cobMainClass.getSelectedItem().toString());
// Class-Path一定不要,除非能保证将引用类(即import的类)都联合打包了
JarOutputStream jos = new JarOutputStream(new FileOutputStream(jfcSave.getSelectedFile()), man);
jos.setLevel(Deflater.BEST_COMPRESSION);
BufferedInputStream bis = null;
byte[] cache = new byte[1024];
StringBuffer config = new StringBuffer();
for(String name : filePaths.keySet()) {
bis = new BufferedInputStream(new FileInputStream(filePaths.get(name)), 1024);
config.append(name).append('=').append(bis.available()).append('\n');
jos.putNextEntry(new JarEntry(name));
int count;
while((count = bis.read(cache, 0, 1024)) != -1)
jos.write(cache, 0, count);
jos.closeEntry();
bis.close();
}
jos.flush();
jos.close();
JOptionPane.showMessageDialog(this, "导出成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
} catch(Exception ex) {
JOptionPane.showMessageDialog(this, ex.getMessage(), "异常", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
} } // 添加文件(非文件夹)到集合
private void addFileToList(File file, List<File> fileArr) {
if(file.isDirectory())
for(File child : file.listFiles())
addFileToList(child, fileArr);
else
fileArr.add(file);
} // 去除重复项
private void removeDuplicateItems(List<File> fileArr) {
// 去重复项
Set<String> directories = new HashSet<>();
Set<String> files = new HashSet<>();
for(File file : fileArr)
if(file.isDirectory())
directories.add(file.getAbsolutePath());
else
files.add(file.getAbsolutePath());
//去包含项(先去文件夹再去文件应该更好)
String fpath,dpath;
for(Iterator<String> itf = files.iterator(); itf.hasNext();) {
fpath = itf.next();
for(Iterator<String> itd = directories.iterator(); itd.hasNext();) {
dpath = itd.next();
if(fpath.startsWith(dpath))
itf.remove();
}
}
String dpath1,dpath2;
Set<String> directories1 = new HashSet<>(directories);
for(Iterator<String> itd1 = directories.iterator(); itd1.hasNext();) {
dpath1 = itd1.next();
for(Iterator<String> itd2 = directories1.iterator(); itd2.hasNext();) {
dpath2 = itd2.next();
if(dpath1.equals(dpath2))
continue;
else if(dpath2.startsWith(dpath1))
itd2.remove();
else if(dpath1.startsWith(dpath2))
itd1.remove();
}
}
directories.addAll(directories1); fileArr.clear();
for(String file : files)
fileArr.add(new File(file));
for(String directory : directories)
fileArr.add(new File(directory));
} private JLabel lblTip;
private JButton btnBrowser;
private JFileChooser jfcSelect;
private JTextArea txtFiles;
private JComboBox<String> cobMainClass;
private JButton btnCls;
private JButton btnConfirm;
private JFileChooser jfcSave;
}
FrmMain
这里有我导出的文件(这个是eclipse导出的,它在manifest中加入了classPath没有错误,我有时候加入后有问题)。
欢迎您移步我们的交流群,无聊的时候大家一起打发时间:
或者通过QQ与我联系:
(最后编辑时间2014-03-02 16:12:50)
使用原生Java代码生成可执行Jar包的更多相关文章
- 使用eclipse创建java程序可执行jar包
一.eclipse中,在要打成jar包的项目名上右击,出现如下弹出框,选择“export”: 二.在接下来出现的界面中点击“jar file”,然后next: 三.在接下来出现的界面中,如图所示勾选上 ...
- JAVA生成(可执行)Jar包的全面详解说明 [打包][SpringBoot][Eclipse][IDEA][Maven][Gradle][分离][可执行]
辛苦所得,转载还请注明: https://www.cnblogs.com/applerosa/p/9739007.html 得空整理了关于java 开发中,所有打包方式的 一个操作方法, 有基于ID ...
- Java打包可执行jar包 包含外部文件
外部文件在程序中设置成相对当前工程路径,执行jar包时,将外部文件放在和jar包平级的目录. public class Main { 3 public static void main(String[ ...
- java 生成可执行jar包
jar -cvfm my.jar [配置主函数入口文件] [包] Main-Class: 包名.类名 注意“:”后边有一个空格,类名后边要有回车换行
- java 执行 jar 包中的 main 方法
java 执行 jar 包中的 main 方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar执行后总是运行指定的主方法,如果 jar 中有多 ...
- IntelliJ IDEA导出Java 可执行Jar包
原文:IntelliJ IDEA导出Java 可执行Jar包 保证自己的Java代码是没有问题的,在IDEA里面是可以正常运行的,然后,按下面步骤: 打开File -> Project Stru ...
- eclipse生成【带有外部jar包】的java可执行jar包
之前有写过一篇使用eclipse生成java可执行jar包,但是最近的一次使用中无论如何都不成功,当双击执行打成的jar时,弹出如下错误: could not find the main class: ...
- java -jar 执行jar包出现 java.lang.NoClassDefFoundError
我用idea工具将自己开发java程序打成一个可执行的jar包,当然用eclipse或者直接用jar命令行都无所谓,本质都是将程序归档到一个压缩包,并附带一个说明清单文件. 打jar的操作其实很简单, ...
- java执行jar包出错:Unable to access jarfile
java执行jar包出错:Unable to access jarfile 错误的原因有多种: 1.一般都是路径不正确.在Windows中,正确的路径类似于: java -jar "D:\W ...
随机推荐
- sf2gis@163.com
1.下载boost1.52,http://www.boost.org/.解压文件到d:\boost\boost_1_52_0. 2.下载python2.7.3,http://www.python.or ...
- Nginx——使用 Nginx 提升网站访问速度【转载+整理】
原文地址 本文是写于 2008 年,文中提到 Nginx 不支持 Windows 操作系统,但是现在它已经支持了,此外还支持 FreeBSD,Solaris,MacOS X~ Nginx(" ...
- Can't get Kerberos realm
1. Can't get Kerberos realm 原因分析: 原始代码为: org.apache.hadoop.security.UserGroupInformation.setConfigur ...
- 不依赖三方库从图像数据中获取宽高-gif、bmp、png、jepg
int extract_pic_info(const BYTE *pic, const uint32_t size, int &width, int &height) { ; widt ...
- 公司Docker环境配置
1.安装最新的docker:$ curl -fsSL get.docker.com -o get-docker.sh$ sudo sh get-docker.sh 2.安装docker-compose ...
- Main.storyboard: WKWebView before iOS 11.0 (NSCoding support was broken in previous versions)
在工程里用 故事板写了 wkwebview 如果运行在 ios11以下 就会报这个错误,如果要支持iOS 11 以下的用户,请重写View部分,使用代码调用WKWebView,而不用使用故事版来加 ...
- 判断Android 当前版本是否为debug版本
public static boolean isDebugVersion(Context context) { try { ApplicationInfo info = context.getAppl ...
- Android 组件系列-----Activity初步
在上篇博文讲解了Android的Activity这个组件的启动流程后,接下来我们就来看看我们的Activity与我们的布局文件的关系吧 我们先来看看一个最简单的布局文件的内容: <Relativ ...
- [转]ssh和SSH服务(包含隧道内容)
本文对SSH连接验证机制进行了非常详细的分析,还详细介绍了ssh客户端工具的各种功能,相信能让各位对ssh有个全方位较透彻的了解,而不是仅仅只会用它来连接远程主机. 另外,本人翻译了ssh客户端命令的 ...
- etcd 集群运维实践
etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境.因此围绕 etcd 相关的运维知识就比较重要,etc ...