1. linux允许ROOT登录ftp
# 进入vsftpd目录
cd /etc/vsftpd
# 查看该目录包含的文件
ls
# 进入文件vsftpd.ftpusers,在root前加#注释root
vi vsftpd.ftpusers
# 进入文件vsftpd.user_list,在root前加#注释root
vi vsftpd.user_list
2. 关于只能IP访问,域名不能访问网站的解决
我买的是腾讯云的服务器Cenos 6.5系统,自己配置的DNS域名服务器,nslookup www.xuefeng66.cn能够正常解析为115.159.201.119(若是非权威解析为该结果证明解析还存在问题,需要更改/etc/resolv.conf中的服务器地址,添加你买的域名服务器地址),解析成功后,发现通过IP可以访问,但是通过域名不能访问,终于发现时tomcat的问题
3. RSA简单加密与解密
import java.io.*;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public class RSADemo {
/*
* 产生秘钥
*/
public static void generateKey() {
try {
// 指定算法
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
// 确定密钥大小
kpg.initialize(1024);
// 产生密钥对
KeyPair kp = kpg.genKeyPair();
// 获取公钥
PublicKey publicKey = kp.getPublic();
// 获取私钥
PrivateKey privateKey = kp.getPrivate();
// 保存公钥
FileOutputStream f1 = new FileOutputStream("publicKey.dat");
ObjectOutputStream o1 = new ObjectOutputStream(f1);
o1.writeObject(publicKey);
o1.close();
f1.close();
// 保存私钥
FileOutputStream f2 = new FileOutputStream("privateKey.dat");
ObjectOutputStream o2 = new ObjectOutputStream(f2);
o2.writeObject(privateKey);
o2.close();
f2.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 加密
*/
public static void encrypt() {
// 明文
String s = "Hello World!";
try {
// 获取公钥及参数e,n
FileInputStream f = new FileInputStream("publicKey.dat");
ObjectInputStream oos = new ObjectInputStream(f);
RSAPublicKey publicKey = (RSAPublicKey) oos.readObject();
BigInteger e = publicKey.getPublicExponent();
BigInteger n = publicKey.getModulus();
System.out.println("参数e= " + e);
System.out.println("参数n= " + n);
// 获取明文
byte[] content = s.getBytes("UTF-8");
BigInteger m = new BigInteger(content);
// 计算密文
BigInteger c = m.modPow(e, n);
System.out.println("密文为:" + c);
// 保存密文
String c1 = c.toString();
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("encrypt.dat")));
out.write(c1, 0, c1.length());
out.close();//一定要记得关闭,否则会出现空指针异常
} catch (Exception e) {
e.printStackTrace();
}
}
public static void decrypt() {
try {
// 读取密文
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream("encrypt.dat")));
String content = br.readLine();
BigInteger c = new BigInteger(content);
// 读取私钥
FileInputStream f1 = new FileInputStream("privateKey.dat");
ObjectInputStream o1 = new ObjectInputStream(f1);
RSAPrivateKey privateKey = (RSAPrivateKey) o1.readObject();
// 获取私钥参数及解密
BigInteger d = privateKey.getPrivateExponent();
BigInteger n = privateKey.getModulus();
System.out.println("参数d=" + d);
System.out.println("参数n=" + n);
BigInteger m = c.modPow(d, n);
// 显示解密结果
byte[] mt = m.toByteArray();
for (int i = 0; i < mt.length; i++) {
System.out.print((char) mt[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
generateKey();
encrypt();
decrypt();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. DES对称密码体系加密解密
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class DESDemo {
/*
* 将字节数组转换为十六进制字符串
*/
public static String byteToHexString(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String string = Integer.toHexString(0XFF & bytes[i]);
if (string.length() == 1) { // 十六进制占四个字节,
stringBuffer.append(0);
}
stringBuffer.append(string.toUpperCase());
}
return stringBuffer.toString();
}
/*
* 加密方法
*/
public static byte[] DES_CBC_Encrypt(byte[] content, byte[] keyBytes) {
try {
// 创建一个 DESKeySpec 对象,使用 key 中的前 8 个字节作为 DES 密钥的密钥内容
DESKeySpec keySpec = new DESKeySpec(keyBytes);
// 返回转换指定算法的秘密密钥的 SecretKeyFactory 对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 根据提供的密钥规范(密钥材料)生成 SecretKey 对象。
SecretKey key = keyFactory.generateSecret(keySpec);
// 返回实现指定转换的 Cipher 对象。
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
// 用密钥和一组算法参数初始化此 Cipher。
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(keySpec.getKey()));
// 按单部分操作加密数据
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/*
* 解密方法
*/
public static byte[] DES_CBC_Decrypt(byte[] content, byte[] keyBytes) {
try {
DESKeySpec keySpec = new DESKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(keyBytes));
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String content = "aaaaaaaabbbbbbbbaaaaaaaa";
String key = "01234567";
System.out.println("加密前:" + byteToHexString(content.getBytes()));
byte[] encrypted = DES_CBC_Encrypt(content.getBytes(), key.getBytes());
System.out.println("加密后:" + byteToHexString(encrypted));
byte[] decrypted = DES_CBC_Decrypt(encrypted, key.getBytes());
System.out.println("解密后:" + byteToHexString(decrypted));
}
}
5. 基于TCP的客户端与服务器端之间的通信
使用说明:把服务器IP地址更改为自己的服务器主机IP地址即可
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
public class Chat extends JFrame implements Runnable, ActionListener {
private Panel topPanel_1, downPanel_1, midPanel_left, midPanel_right;
private Label ipLabel, localNameLabel;
private TextField ipTextField, localNameField;
private Button createServer, searchServer, sendMessage;
private TextArea text1, text2, text3, text4;
private ServerSocket server;
private Socket serverSocket, clientSocket;
private DataOutputStream outputFromClient, outputFromServer;
private DataInputStream inputToClient, inputToServer;
private int scan = 2;// scan:便于区分0客户端与1服务器端的文本内容
private int lock=0;//0:创建服务器 1:停止服务器
/***************************** 获取主机IP 与 名称 ******************************/
public String getIp() {
String ip = null;
try {
InetAddress myLocalHost = InetAddress.getLocalHost();
ip = myLocalHost.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return ip;
}
public String getName() {
String name = null;
try {
InetAddress myLocalHost = InetAddress.getLocalHost();
name = myLocalHost.getHostName();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return name;
}
/***************************** 事件监听 *****************************/
public void message() {
createServer.addActionListener(this);
searchServer.addActionListener(this);
sendMessage.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == createServer && lock==0) {
text3.setText("");
text3.append("服务器名称为:" + getName() + "\n");
text3.append("服务器IP为:" + getIp() + "\n");
text3.append("端口号为:6666\n");
text3.append("服务器已经启动,正在监听.......\n");
this.startServer();
scan=1;
} else if(e.getSource() == createServer && lock==1){
try {
serverSocket.close();
text3.setText("");
text3.append("服务器关闭成功");
lock=0;
} catch (IOException e1) {
text3.append("服务器关闭异常");
e1.printStackTrace();
}
}else if (e.getSource() == searchServer) {
text4.setText("");
text4.append("正在搜索服务器,请稍等.....\n");
this.startClient();
scan=0;
} else if (e.getSource() == sendMessage) {
if (scan == 1) {// 服务器端
try {
outputFromServer = new DataOutputStream(
serverSocket.getOutputStream());
String name = getName();
if (text2.getText().length() > 0) {
text1.append(name + "说: " + text2.getText()
+ "\n");
outputFromServer.writeUTF(this.getName()+","+text2.getText());// 写入消息
text2.setText("");
} else {
text2.setText("\n\n请输入内容\n\n");
Thread.sleep(1000);
text2.setText("");
}
} catch (InterruptedException | IOException e1) {
e1.printStackTrace();
}
} else if (scan == 0 ) {// 客户端
try {
outputFromClient = new DataOutputStream(
clientSocket.getOutputStream());
String name = getName();
if (text2.getText().length() > 0 ) {
text1.append(name + "说: " + text2.getText()+ "\n");
outputFromClient.writeUTF(this.getName()+","+text2.getText());// 写入消息
text2.setText("");
} else if(text2.getText().length() <= 0) {
text2.setText("\n\n请输入内容\n\n");
Thread.sleep(1000);
text2.setText("");
}
} catch (InterruptedException | IOException e1) {
e1.printStackTrace();
}
}
}
}
/***************************** 启动服务器 *************************************/
public void startServer() {
try {
if(lock==0){
server = new ServerSocket(6666);
serverSocket = server.accept();
createServer.setLabel("停止服务器");
lock=1;
}
} catch (IOException e) {
text3.setText("");
text3.append("服务器启动错误,请重新设置后启动!\n可能是由于:\n");
text3.append("1.端口被占用。\n");
text3.append("2.服务器已经启动。\n");
e.printStackTrace();
}
}
/***************************** 启动客户端 *************************************/
public void startClient() {
try {
clientSocket = new Socket("192.168.31.125",6666);//更改为自己服务器主机的IP地址即可
text4.append("连接成功 ");
searchServer.setLabel("断开连接");
} catch (Exception e) {
text4.append("无法连接网络");
e.printStackTrace();
}
}
/******************************* 对话内容互相显示 *****************************/
public void messageDisplay() throws IOException {
// 接收消息
if (scan == 1) {// 客户端
inputToClient = new DataInputStream(serverSocket.getInputStream());
String receive = inputToClient.readUTF();
String[] message=receive.split(",");
text1.append(message[0]+"说: "+message[1]+"\n");
}else if(scan == 0){//服务器端
inputToServer = new DataInputStream(clientSocket.getInputStream());
String receive = inputToServer.readUTF();
String[] message=receive.split(",");
text1.append(message[0]+"说: "+message[1]+"\n");
}
}
/***************************** 创建主界面 *************************************/
private void launchFrame() {
// /上面部分/
topPanel_1 = new Panel();
ipLabel = new Label("IP地址");// 标签
ipTextField = new TextField(getIp(), 19);
localNameLabel = new Label("本机名称");
localNameField = new TextField(getName(), 19);
createServer = new Button("创建服务器");
searchServer = new Button("搜索服务器");
// /中部部分/
midPanel_left = new Panel();
midPanel_right = new Panel();
text1 = new TextArea(20, 68);
text2 = new TextArea(3, 68);
text3 = new TextArea(14, 25);
text4 = new TextArea(9, 25);
// /底部部分/
downPanel_1 = new Panel();
sendMessage = new Button("发送");
topPanel_1.add(ipLabel);// 加入面板
topPanel_1.add(ipTextField);
topPanel_1.add(localNameLabel);
topPanel_1.add(localNameField);
topPanel_1.add(createServer);
topPanel_1.add(searchServer);
midPanel_left.setLayout(new BorderLayout());
midPanel_right.setLayout(new BorderLayout());
midPanel_left.add(text1, BorderLayout.NORTH);
midPanel_left.add(text2, BorderLayout.SOUTH);
midPanel_right.add(text3, BorderLayout.NORTH);
midPanel_right.add(text4, BorderLayout.SOUTH);
downPanel_1.add(sendMessage);
Container container = getContentPane();// 布局管理器
container.setLayout(new BorderLayout());// 布局声明
container.add(topPanel_1, BorderLayout.NORTH);
container.add(midPanel_left, BorderLayout.CENTER);
container.add(midPanel_right, BorderLayout.EAST);
container.add(downPanel_1, BorderLayout.SOUTH);
this.pack();// 调整此窗口的大小,以适合其子组件的首选大小和布局。
setSize(700, 500);// 设置面板宽与高
setTitle("点星光聊天");// 设置标题
setResizable(false);// 大小不可变
setVisible(true);// 设置面板可见
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 关闭时释放资源
}
public static void main(String[] args) throws IOException {
Chat chat = new Chat();
chat.launchFrame();
chat.message();
while(true){
chat.messageDisplay();
}
}
@Override
public void run() {
// TODO 自动生成的方法存根
}
}
6. 红蓝按钮交替移动
编写一个应用程序,除了主线程外,还有两个线程:first和second。first负责模拟一个红色的按钮从坐标(10,60)运动到(100,60);second负责模拟一个绿色的按钮从坐标(100,60)运动到(200,60)。另外还有一个start按钮,当点击start按钮后,红色按钮平行移动从左边移动到右边,当红色按钮移动到绿色按钮位置后,红色按钮停止在绿色按钮起始位置,然后绿色按钮接着移动。当绿色按钮移动到指定位置后,所有按钮位置重置,然后循环执行上述过程。
public class MoveButton extends JFrame implements Runnable, ActionListener {
private int distance = 10;
Thread first, second;
Button redButton, greenButton, startButton;
public MoveButton() {
/*
* 创建线程
*/
first = new Thread(this);
second = new Thread(this);
setLayout(null); // 清除默认布局
/*
* 设置红色按钮的颜色
* 设置起始位置与大小
* 加入窗体
*/
redButton = new Button();
redButton.setBackground(Color.red);
redButton.setBounds(10, 60, 15, 15);
add(redButton);
/*
* 设置红色按钮的颜色
* 设置起始位置与大小
* 加入窗体
*/
greenButton = new Button();
greenButton.setBackground(Color.green);
greenButton.setBounds(100, 60, 15, 15);
add(greenButton);
/*
* 设置开始按钮的起始位置与大小
* 添加监听器
* 加入窗体
*/
startButton = new Button("start");
startButton.addActionListener(this);
startButton.setBounds(10, 100, 30, 30);
add(startButton);
setBounds(0, 0, 300, 200);// 设置窗体的起始位置与长宽
setVisible(true);// 设置窗体可见
/*
* 关闭窗时释放内存资源
*/
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/*
* 实现按钮的移动
* synchronized 方法控制对类成员变量的访问
*/
private synchronized void moveComponent(Button button) {
if (Thread.currentThread() == first) {
while (distance > 100 && distance <= 200) {
try {
wait();
System.out.println("你好");//线程等待,直到其他线程调用notify或notifyAll方法通知该线程醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
distance += 1;
button.setLocation(distance, 60);
if (distance >= 100) {
if (distance <= 200) {
button.setLocation(100, 60);//在蓝色按钮运动期间红色按钮始终位于蓝色按钮最初位置
} else {
button.setLocation(10, 60);//当距离移动距离大于200时,蓝色按钮归位
}
notify();//唤醒单个等待的线程(由于约束条件的存在,此程序中势必只有一个等待的线程,故可用此方法替换)
//notifyAll();//唤起全部等地的线程
}
}
if (Thread.currentThread() == second) {
while (distance >= 10 && distance < 100) {
try {
wait();//线程等待,直到其他线程调用notify或notifyAll方法通知该线程醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
distance += 1;
button.setLocation(distance, 60);
if (distance > 200) {
button.setLocation(100, 60);//当距离移动距离大于200时,蓝色按钮归位
distance = 10;//distance置初值
notify();//
}
}
}
public void run() {
while (true) {
/*
* 判断当前执行的线程是否为first
* 如果是,调用moveComponent()方法,移动redButton
* 线程睡眠20ms
*/
if (Thread.currentThread() == first) {
moveComponent(redButton);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
* 判断当前执行的线程是否为second
* 如果是,调用moveComponent()方法,移动greenButton
* 线程睡眠10ms
*/
if (Thread.currentThread() == second) {
moveComponent(greenButton);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
* 事件监听,启动线程(由于只对startButton按钮绑定监听器,所以默认监听该按钮)
*/
public void actionPerformed(ActionEvent e) {
try {
first.start();//启动线程
second.start();
} catch (Exception e1) {
e1.printStackTrace();
}
}
public static void main(String[] args) {
new MoveButton();
}
}
7. JAVA根据指定URL生成二维码
public class QrCodeUtil {
public static void main(String[] args) {
String url = "https://www.baidu.com";
String path = FileSystemView.getFileSystemView().getHomeDirectory() + File.separator + "testQrcode";
String fileName = "temp.jpg";
createQrCode(url, path, fileName);
}
public static String createQrCode(String url, String path, String fileName) {
try {
Map<EncodeHintType, String> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 400, 400, hints);
File file = new File(path, fileName);
if (file.exists() || ((file.getParentFile().exists() || file.getParentFile().mkdirs()) && file.createNewFile())) {
writeToFile(bitMatrix, "jpg", file);
System.out.println("搞定:" + file);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
BufferedImage image = toBufferedImage(matrix);
if (!ImageIO.write(image, format, file)) {
throw new IOException("Could not write an image of format " + format + " to " + file);
}
}
static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
BufferedImage image = toBufferedImage(matrix);
if (!ImageIO.write(image, format, stream)) {
throw new IOException("Could not write an image of format " + format);
}
}
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
}
}
return image;
}
}
8. ElementPlus中el-select在IOS中无法唤醒软件盘解决方案
项目基于Vue3 + ElementPlus1.3
在main.js代码中
import ElementPlus from 'element-plus'
/* eslint-disable no-new */
window.$vueApp = Vue.createApp(App)
// 给组件每个生命周期,都混入一些公共逻辑, 解决IOS el-select无法唤醒软件盘问题
window.$vueApp.mixin({
mounted () {
if (typeof this.$el.className === 'string') {
if (this.$el.className.split(' ').indexOf('el-select') !== -1) {
this.$el.children[0].children[0].children[0].removeAttribute('readOnly')
this.$el.children[0].children[0].children[0].onblur = function () {
let _this = this
setTimeout(() => {
_this.removeAttribute('readOnly')
}, 200)
}
}
}
}
})
window.$vueApp.use(ElementPlus)
9. JAVA流实现文件批量打包下载
@ResponseBody
public void downloadFiles(HttpServletRequest request, HttpServletResponse response, String[] filePaths) {
if (filePaths == null || filePaths.length <= 0) {
return ;
}
// 设置响应头
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
// 设置压缩包名称及不同浏览器中文乱码处理
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.now();
String filename = "电子合同" + formatter.format(localDateTime) + ".zip";
String agent = request.getHeader("AGENT");
try {
if (agent.contains("MSIE") || agent.contains("Trident")) {
filename = URLEncoder.encode(filename, "UTF-8");
} else {
filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setHeader("Content-Disposition", "attachment;filename=\"" + filename + "\""); // key不区分大小写
try {
// 设置压缩流,直接写入response,实现边压缩边下载
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));
zipOutputStream.setMethod(ZipOutputStream.DEFLATED);
DataOutputStream dataOutputStream = null;
for (String filePath : filePaths) {
String subFilename = formatter.format(LocalDateTime.now()) + filePath.substring(filePath.lastIndexOf("/") + 1);
zipOutputStream.putNextEntry(new ZipEntry(subFilename));
dataOutputStream = new DataOutputStream(zipOutputStream);
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(new File(filePath)));
byte[] buf = new byte[8192];
int length = 0;
while ((length = bufferedInputStream.read(buf)) != -1) {
dataOutputStream.write(buf, 0 , length);
}
dataOutputStream.flush();
// dataOutputStream.close(); 若在此关闭,对应资源zipOutputStream也将关闭,则压缩包内仅有一个文件
bufferedInputStream.close();
zipOutputStream.closeEntry();
}
dataOutputStream.flush();
dataOutputStream.close();
zipOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
10. Windows10强制停止Vmmem
wsl --shutdown
11. Windows10强制删除占用端口的进程
# 查看占用端口8082的进程
netstat -ano|findstr 8082
# 强制删除进程20380及子进程
taskkill /T /F /PID 20380
12. PostgreSQL强制删除数据库
12.1 设置数据库为禁止连接
UPDATE pg_database
SET datallowconn = 'false'
WHERE datname = 'db_name';
12.2 中断当前库中所有连接会话
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'db_name';
12.3 删除数据库
drop database db_name;
13. 基于seaborn的正太分布图
import matplotlib.pyplot as plt
import seaborn as sns
# rating_df类型为pandas
sns.kdeplot(rating_df['rating_our'], fill=True, shade=True, bw=0.8, color='#FA705C')
sns.kdeplot(rating_df['rating_customer'], fill=True, shade=True, bw=0.8, color='red')
plt.show()
14. 微信小程序文件下载两种方式
14.1 基本url方式下载(自定义下载文件名称)
downloadDailyYear: function() {
util.get(api.downloadDailyYear).then(function(res) { // 自定义get请求,可忽略
if (res.code == 200) {
var fileName = res.data.substring(res.data.lastIndexOf("/") + 1, res.data.indexOf("."));
wx.downloadFile({
url: api.appUrl + res.data,
// 1. 必须带有这个wx.env.USER_DATA_PATH,表示存储在用户本地 !!!
// fileName表示自定的文件名称
// 实际在PC端调试存储位置为类似 C:\Users\SJshe\AppData\Local\微信开发者工具\User Data\WeappFileSystem\o6zAJs3c0u3SeBVn_9MUgG6UZJ1M\wx2efdf4edd8bccb88
filePath: wx.env.USER_DATA_PATH + "/" + fileName,
success: function (res) {
if (res.statusCode === 200) {
wx.openDocument({
filePath: res.filePath,
fileType: ['xlsx'], // 2. 这个必须写合法类型,不然下载不了(个人认为官方应该特别说明) !!!
success: function (res) {
console.log('打开文档成功')
},
fail: function(e) {
console.log(e.errMsg);
}
})
}
},
fail: function(e) { // 强烈建议打印失败原因,便于排查
console.log(e.errMsg);
}
});
}
});
},
14.2 基于后台返回流的方式下载
@GetMapping(value = "/downloadDailyYear")
public BaseResponse<byte[]> downloadDailyYear(HttpSession session, @RequestParam String id,
@RequestParam @DateTimeFormat(iso = ISO.DATE) Date startDate,
@RequestParam @DateTimeFormat(iso = ISO.DATE) Date endDate) {
}
public class BaseResponse<T> {
private String code;
private String message;
private T data;
...
}
downloadDailyYear: function () {
var name = '';
wx.getStorage({
key: 'userInfo',
success(res) {
name += res.data.name + res.data.employeeNo;
}
})
var year = new Date().getFullYear();
util.get(api.downloadDailyYear, {
'id': wx.getStorageSync('userId'),
'startDate': year + "-" + "01-01",
'endDate': year + "-" + "12-31"
}).then(function (res) {
if (res.code == 200) {
// 1. 必须带有这个wx.env.USER_DATA_PATH,表示存储在用户本地 !!!
var filePath = wx.env.USER_DATA_PATH + '/' + year + '工作周报-' + name;
FileSystemManager.writeFile({
filePath: filePath,
data: res.data,
encoding: 'base64', // 2. base64解密写入, 后台返回的byte[]数组是经过base64编码的,其他方式写入文件打开格式不对
success: function(res) {
wx.openDocument({
filePath: wx.env.USER_DATA_PATH + '/' + year + '工作周报-' + name,
fileType: ['xlsx'], // 3. 这个必须写合法类型,不然下载不了 !!!
success: function (res) {
console.log('打开文档成功')
},
fail: function (e) {
console.log(e.errMsg);
}
})
},
fail: function (e) {
console.log(e.errMsg);
}
});
}
});
},