很久没来写文章了,自己忙又懒。最近实现了一个需求,有点意思,就想起来记录一下。

业务需求:生成一个二维码,旁边还要加点解释说明什么的,(类似)最终效果如下

一.实现思路

1.二维码生成:使用hutool提供的google二维码生成工具

2.背景图(那个纯白色背景)+文字:使用java自带的Graphics绘制工具

3.批量下载:选择使用java自带的ZipOutputStream压缩流工具

4.性能问题:全部图片的操作都在内存上操作,性能不错,但耗内存

二.实现步骤(编码)

随便拿个springBoot的工程来写个demo

1.添加用到的依赖(pom.xml):

        <!-- lombok工具类 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency><!-- google工具类 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version></dependency><!-- 生成二维码所需 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency><!-- hutool工具类 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>4.5.15</version></dependency>

2.编写两个工具类

①.QrCodeUtils.java,图片相关操作以及文件下载工具类(当然,可以再进行分割,这里是demo,就随便封装一下)

package com.cloud.sbjm.common;import com.cloud.sbjm.onput.vo.DownloadFile;
import com.cloud.sbjm.onput.vo.MessageQrCodeVo;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import cn.hutool.extra.qrcode.QrCodeUtil;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;/*** @author :ouyangzhicheng* @date :Created in 2019-9-17 14:54* @description:图片工具类* @version: 1.0.0*/
@Component
@Slf4j
public class QrCodeUtils {private static final String MESSAGE_NUM = "XX编码";private static final String MESSAGE_CATEGORY = "XX类别";private static final String MESSAGE_QRCODE = "XX二维码";private static final String FONT_TYPE = "宋体";private static String MESSAGE_QRCODE_URL;private static final String DISPLAYNAME = "XX二维码.png";@Value("${message-qrcode-url}")public void setMESSAGE_QRCODE_URL(String MESSAGE_QRCODE_URL) {this.MESSAGE_QRCODE_URL = MESSAGE_QRCODE_URL;}/*** 生成资产管理二维码图片*/public static InputStream generateAssetsQrCodeImage(MessageQrCodeVo messageQrCodeVo) throws IOException{// 获取图片的缓冲区,也就是所谓的画布BufferedImage bufferedImage = new BufferedImage(600, 350, BufferedImage.TYPE_INT_RGB);//获取画笔,画笔用于在画布上进行绘制Graphics paint = bufferedImage.getGraphics();//设置画笔的颜色paint.setColor(Color.white);//绘制画布的背景色paint.fillRect(0, 0, 600, 350);//生成二维码图片字节流byte[]  qrCodeFile = QrCodeUtil.generatePng(MESSAGE_QRCODE_URL+"?id="+messageQrCodeVo.getId(), 180, 180);return overlapImage(bufferedImage,qrCodeFile,messageQrCodeVo);}/*** 图片重叠* @param bufferedImage* @param qrCodeFile* @param messageQrCodeVo* @return*/public static InputStream overlapImage(BufferedImage bufferedImage, byte[]  qrCodeFile, MessageQrCodeVo messageQrCodeVo) throws IOException {ByteArrayOutputStream os = null;InputStream qrCodeInputStream =null;try {BufferedImage qrCode = ImageIO.read(new ByteArrayInputStream(qrCodeFile));//在背景图片中添加入需要写入的信息Graphics2D g = bufferedImage.createGraphics();g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);g.setColor(Color.black);g.setFont(new Font(FONT_TYPE,Font.BOLD,25));g.drawString(messageQrCodeVo.getName(),240 ,120);g.setColor(Color.gray);g.setFont(new Font(FONT_TYPE,Font.BOLD,20));g.drawString(MESSAGE_NUM,240 ,160);g.drawString(MESSAGE_CATEGORY,240 ,200);g.drawString(MESSAGE_QRCODE,70 ,270);g.setColor(Color.black);g.drawString(messageQrCodeVo.getNumber(),345 ,160);g.drawString(messageQrCodeVo.getTypeName(),345 ,200);//在背景图片上添加二维码图片g.drawImage(qrCode, 30, 65, qrCode.getWidth(), qrCode.getHeight(), null);g.dispose();os = new ByteArrayOutputStream();ImageIO.write(bufferedImage, "png", os);qrCodeInputStream = new ByteArrayInputStream(os.toByteArray());}catch (Exception e){log.warn("生成二维码出错:"+e.getMessage());}finally {if(os!=null){os.close();}if(qrCodeInputStream!=null){qrCodeInputStream.close();}}return qrCodeInputStream;}/*** 资产二维码下载(包含文字信息)*/public static void messageQrCodeDownload(HttpServletResponse response, List<MessageQrCodeVo> messageQrCodeVos) throws IOException {InputStream qrCodeInputStream = null;OutputStream outputStream = null;try (Closer closer = Closer.create()) {//单个二维码导出if(messageQrCodeVos.size() == 1){qrCodeInputStream = generateAssetsQrCodeImage(messageQrCodeVos.get(0));closer.register(qrCodeInputStream);// String charset = StandardCharsets.UTF_8.displayName();String charset = "GB2312";response.setCharacterEncoding(charset);response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());String filename = new String(DISPLAYNAME.getBytes(charset), StandardCharsets.ISO_8859_1);response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);outputStream = closer.register(response.getOutputStream());ByteStreams.copy(qrCodeInputStream, outputStream);outputStream.flush();}//批量二维码导出if(messageQrCodeVos.size()>1){response.setContentType("application/zip");response.setHeader("Content-Disposition", "attachment; filename=QrCodes.zip");List<DownloadFile> downloadFiles =new ArrayList<>();int i = 0;for(MessageQrCodeVo messageQrCodeVo:messageQrCodeVos){i++;DownloadFile downloadFile = new DownloadFile();downloadFile.setFileName("XX二维码"+i+".png");downloadFile.setInputStream(generateAssetsQrCodeImage(messageQrCodeVo));downloadFiles.add(downloadFile);}ZipUtil.toZip3(downloadFiles, response.getOutputStream());}}catch (Exception e){log.warn("二维码下载出错:"+e.getMessage());}finally {if(qrCodeInputStream!=null){qrCodeInputStream.close();}if(outputStream!=null){outputStream.close();}}}/*** 普通文件下载*/public static void imageDownload(HttpServletResponse response, InputStream inputStream, String fileName) throws IOException {OutputStream outputStream = null;try (Closer closer = Closer.create()) {closer.register(inputStream);String charset = "GB2312";response.setCharacterEncoding(charset);response.setContentType(MediaType.IMAGE_PNG.toString());String filename = new String(fileName.getBytes(charset), StandardCharsets.ISO_8859_1);response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);outputStream = closer.register(response.getOutputStream());ByteStreams.copy(inputStream, outputStream);outputStream.flush();}catch (Exception e){log.warn("图片下载出错:"+e.getMessage());}finally {if(inputStream!=null){inputStream.close();}if(outputStream!=null){outputStream.close();}}}
}

②.ZipUtil.java,压缩文件操作工具类

package com.cloud.sbjm.common;import com.cloud.sbjm.onput.vo.DownloadFile;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;/*** @author :ouyangzhicheng* @date :Created in 2019-9-24 16:03* @description:压缩文件处理* @version: 1.0.0*/
public class ZipUtil {/*** 解压缩zip包** @param zipFilePath        zip文件的全路径* @param unzipFilePath      解压后的文件保存的路径* @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名。true-包含;false-不包含,目前只支持true* @param charset            The {@linkplain Charset charset} to be*                           used to decode the ZIP entry name and comment (ignored if*                           the <a href="package-summary.html#lang_encoding"> language*                           encoding bit</a> of the ZIP entry's general purpose bit*                           flag is set*/public static List<String> unZip(String zipFilePath, String unzipFilePath, boolean includeZipFileName, Charset charset) throws IOException {List<String> filePaths = new ArrayList<>(32);if (Strings.isNullOrEmpty(zipFilePath)) {return filePaths;}File zipFile = new File(zipFilePath);// 如果解压后的文件保存路径包含压缩文件的文件名,则追加该文件名到解压路径String fileName = zipFile.getName();int pos;if (includeZipFileName) {pos = fileName.lastIndexOf(".");if (pos > 0) {fileName = fileName.substring(0, pos);}unzipFilePath = unzipFilePath + File.separator + fileName;}// 开始解压try (ZipFile zip = new ZipFile(zipFile, charset); Closer closer = Closer.create()) {for (Enumeration<? extends ZipEntry> zipEntries = zip.entries(); zipEntries.hasMoreElements(); ) {ZipEntry zipEntry = zipEntries.nextElement();if (zipEntry.isDirectory()) {continue;}String zipEntryName = zipEntry.getName();String outPath = unzipFilePath + File.separator + zipEntryName;File outFile = new File(outPath);//目录不存在就创建吧com.google.common.io.Files.createParentDirs(outFile);BufferedInputStream bufferedIn = closer.register(new BufferedInputStream(zip.getInputStream(zipEntry)));BufferedOutputStream bufferedOut = closer.register(new BufferedOutputStream(new FileOutputStream(outFile)));ByteStreams.copy(bufferedIn, bufferedOut);filePaths.add(outPath.replace("\\", "/"));}}return filePaths;}/*** 解压,返回所有文件的路径** @param zipFilePath        压缩包的路径* @param unZipFilePath      解压之后的目录* @param includeZipFileName 解压之后是否用压缩包名作为父目录* @return 返回文件的路径* @throws IOException I/O异常*/public static List<String> unZip(String zipFilePath, String unZipFilePath, boolean includeZipFileName) throws IOException {Charset defaultCharset = Charset.forName("GBK");return unZip(zipFilePath, unZipFilePath, includeZipFileName, defaultCharset);}private static final int BUFFER_SIZE = 2 * 1024;/*** 压缩成ZIP 方法1** @param srcDir           压缩文件夹路径* @param out              压缩文件输出流* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;*                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)* @throws RuntimeException 压缩失败会抛出运行时异常*/public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure) throws RuntimeException {long start = System.currentTimeMillis();ZipOutputStream zos = null;try {zos = new ZipOutputStream(out);File sourceFile = new File(srcDir);compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);long end = System.currentTimeMillis();System.out.println("压缩完成,耗时:" + (end - start) + " ms");} catch (Exception e) {throw new RuntimeException("zip error from ZipUtils", e);} finally {if (zos != null) {try {zos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 压缩成ZIP 方法2** @param srcFiles 需要压缩的文件列表* @param out      压缩文件输出流* @throws RuntimeException 压缩失败会抛出运行时异常*/public static void toZip(List<File> srcFiles, List<String> fileNames, OutputStream out) throws RuntimeException {long start = System.currentTimeMillis();ZipOutputStream zos = null;int i = 0;try {zos = new ZipOutputStream(out);FileInputStream in=null;for (File srcFile : srcFiles) {byte[] buf = new byte[BUFFER_SIZE];zos.putNextEntry(new ZipEntry(fileNames.get(i)));i++;int len;try{in = new FileInputStream(srcFile);while ((len = in.read(buf)) != -1) {zos.write(buf, 0, len);}}finally {zos.closeEntry();if(in!=null){in.close();}}}long end = System.currentTimeMillis();System.out.println("压缩完成,耗时:" + (end - start) + " ms");} catch (Exception e) {throw new RuntimeException("zip error from ZipUtils", e);} finally {if (zos != null) {try {zos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 压缩成ZIP 方法3* @param downloadFiles 需要压缩的文件列表* @param out      压缩文件输出流* @throws RuntimeException 压缩失败会抛出运行时异常*/public static void toZip3(List<DownloadFile> downloadFiles, OutputStream out) throws RuntimeException {long start = System.currentTimeMillis();ZipOutputStream zos = null;try {zos = new ZipOutputStream(out);InputStream in=null;for (DownloadFile downloadFile : downloadFiles) {byte[] buf = new byte[BUFFER_SIZE];zos.putNextEntry(new ZipEntry(downloadFile.getFileName()));int len;try{in = downloadFile.getInputStream();while ((len = in.read(buf)) != -1) {zos.write(buf, 0, len);}}finally {zos.closeEntry();in.close();}}long end = System.currentTimeMillis();System.out.println("压缩完成,耗时:" + (end - start) + " ms");} catch (Exception e) {throw new RuntimeException("zip error from ZipUtils", e);} finally {if (zos != null) {try {zos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 递归压缩方法** @param sourceFile       源文件* @param zos              zip输出流* @param name             压缩后的名称* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;*                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)* @throws Exception*/private static void compress(File sourceFile, ZipOutputStream zos, String name, boolean KeepDirStructure) throws Exception {byte[] buf = new byte[BUFFER_SIZE];FileInputStream in=null;try {if (sourceFile.isFile()) {// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字zos.putNextEntry(new ZipEntry(name));// copy文件到zip输出流中int len;in = new FileInputStream(sourceFile);while ((len = in.read(buf)) != -1) {zos.write(buf, 0, len);}// Complete the entryzos.closeEntry();in.close();} else {File[] listFiles = sourceFile.listFiles();if (listFiles == null || listFiles.length == 0) {// 需要保留原来的文件结构时,需要对空文件夹进行处理if (KeepDirStructure) {// 空文件夹的处理zos.putNextEntry(new ZipEntry(name + "/"));// 没有文件,不需要文件的copyzos.closeEntry();}} else {for (File file : listFiles) {// 判断是否需要保留原来的文件结构if (KeepDirStructure) {// 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,// 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了compress(file, zos, name + "/" + file.getName(), KeepDirStructure);} else {compress(file, zos, file.getName(), KeepDirStructure);}}}}}finally {if(in !=null){in.close();}}}}

以上2个工具写好了,就完成了大部分的流程了。

3.编写Controller、Service,将业务模拟出来

①FileController.java

package com.cloud.sbjm.boot;import com.cloud.sbjm.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletResponse;/*** @author :ouyangzhicheng* @date :Created in 2019-10-10 9:30* @description:文件处理* @version: 1.0.0*/
@Controller
@RequestMapping(value = "/file")
public class FileController {@Autowiredprivate FileService fileService;/*** 生成简单二维码(单个)* @author: oyzc* @date: 2019-09-10*/@GetMapping(value = "/getQrCode")public void getQrCode(HttpServletResponse httpResponse, String id){fileService.getQrCode(httpResponse,id);}/*** 导出包含其他文字信息二维码(单个/批量)* @author: oyzc* @date: 2019-09-10*/@GetMapping(value = "/generateMessageQrCode")public void generateMessageQrCode(HttpServletResponse httpResponse, String[] ids){fileService.generateMessageQrCode(httpResponse,ids);}
}

②.FileService.java

package com.cloud.sbjm.service;import javax.servlet.http.HttpServletResponse; /*** @author :ouyangzhicheng* @date :Created in 2019-10-10 9:32* @description:文件处理实现类* @version: 1.0.0*/
public interface FileService {/*** 导出包含其他文字信息二维码(单个/批量)* @author: oyzc* @date: 2019-09-10*/void generateMessageQrCode(HttpServletResponse httpResponse, String[] ids);/*** 生成简单二维码(单个)* @author: oyzc* @date: 2019-09-10*/void getQrCode(HttpServletResponse httpResponse, String id);
}

③.FileServiceImpl.java

package com.cloud.sbjm.service.Imp;import cn.hutool.extra.qrcode.QrCodeUtil;
import com.cloud.sbjm.common.QrCodeUtils;
import com.cloud.sbjm.onput.vo.MessageQrCodeVo;
import com.cloud.sbjm.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @author :ouyangzhicheng* @date :Created in 2019-10-10 9:33* @description:文件处理实现类* @version: 1.0.0*/
@Slf4j
@Service
public class FileServiceImpl implements FileService{/*** 导出包含其他文字信息二维码(单个/批量)* @author: oyzc* @date: 2019-09-10*/@Value("${message-qrcode-url}")private String MESSAGE_QRCODE_URL;@Overridepublic void generateMessageQrCode(HttpServletResponse httpResponse, String[] ids) {if(ids == null){return;}//测试模拟查询数据库(根据业务需求改动)List<MessageQrCodeVo> messageQrCodeVoList = new ArrayList<>();for(String id:ids){MessageQrCodeVo messageQrCodeVo = new MessageQrCodeVo();messageQrCodeVo.setId(id);messageQrCodeVo.setName("XXXXX");messageQrCodeVo.setNumber("XXXXXXXXXXX");messageQrCodeVo.setTypeName("XXXXXXXXXXXXXXX");messageQrCodeVoList.add(messageQrCodeVo);}try {QrCodeUtils.messageQrCodeDownload(httpResponse,messageQrCodeVoList);} catch (IOException e) {log.warn("生成二维码出错:"+e.getMessage());}}/*** 生成简单二维码(单个)* @author: oyzc* @date: 2019-09-10*/@Overridepublic void getQrCode(HttpServletResponse httpResponse, String id) {//生成二维码图片字节流byte[]  qrCodeFile = QrCodeUtil.generatePng(MESSAGE_QRCODE_URL+"?id="+id, 180, 180);try {QrCodeUtils.imageDownload(httpResponse,new ByteArrayInputStream(qrCodeFile),"qrcode.png");} catch (IOException e) {log.warn("获取二维码出错:"+e.getMessage());}}
}

以上基本模拟完一个简单的业务流程,因为是demo,没有涉及dao层,可根据自身业务进行改造

4.相关的vo类和配置文件也贴一下

①.DownloadFile.java

package com.cloud.sbjm.onput.vo;import lombok.Data;import java.io.InputStream;/*** @author :ouyangzhicheng* @date :Created in 2019-9-24 16:14* @description:下载文件列表(流文件)* @version: 1.0.0*/
@Data
public class DownloadFile {/*** 文件名称*/private String fileName;/*** 文件输入流*/private InputStream inputStream;
}

②.MessageQrCodeVo.java

package com.cloud.sbjm.onput.vo;import lombok.Data;/*** @author :ouyangzhicheng* @date :Created in 2019-9-17 19:18* @description:二维码所需参数实体* @version: 1.0.0*/
@Data
public class MessageQrCodeVo {/*** Id*/private String id;/*** 名称*/private String name;/*** 编码*/private String number;/*** 类别*/private String typeName;
}

③.application.yml配置文件,添加一个参数,二维码的链接

message-qrcode-url = www.baidu.com

自此,把所有涉及的代码都贴出来了,各位有需要可以根据自身业务进行改造

三.测试那些事

为了测试,我过滤掉了所有的校验,直接通过浏览器发送请求来进行测试

demo是根据参数ids所传的个数来决定是单个下载还是批量下载

1.单个下载

图片打开后:

2.批量下载(压缩包)

打开文件后:

自此,功能测试通过。

如有错漏,请大伙指正,谢谢。

Java实现二维码和文字合成一张新图片相关推荐

  1. Java生成二维码并贴到新的图片上

    一.简述 1.Java生成二维码以来一些第三方库,本地的api会有一些bug,比如生成图片会造成红色蒙版,还有当二维码中字数过多,会造成二维码大小出现问题,要不就是二维码很大,要不就是二维码白边很大. ...

  2. 微信小程序生成二维码可文字,链接,图片(支持中文)

    功能简介 : 微信小程序生成二维码,支持文本和网址,支持中英文,输入框可清空,可单击保存二维码...... 核心代码 : createQrCode:function(url,canvasId,cavW ...

  3. java生成二维码合成背景图

    一.背景 接到个需求,要打印纸质红包,上面附二维码,然后每个红包的二维码不一样,扫描二维码去参加活动领真实的红包,需要给出对应的图片 二.代码示例 import java.awt.Graphics2D ...

  4. Java实现一行代码生成二维码,可传输到前端展示,可自定义二维码样式,可设置图片格式,可对二维码添加图片,可对二维码添加文字,可以设置二维码大小、字体大小、字体颜色、边框颜色、边框大小等等

    Java实现一行代码生成二维码,可传输到前端展示,可自定义二维码样式,可设置图片格式,可对二维码添加图片,可对二维码添加文字,可以设置二维码大小.字体大小.字体颜色.边框颜色.边框大小等等. 0.准备 ...

  5. Java生成二维码底部带文字并且返回前端使用img接收

    目录 1.java生成二维码工具类 2.web测试 3.前端处理 4.测试结果 背景 本demo主要针对jdk1.6版本的,但是高版本的同样可以用,如果觉得不舒服可以自行添加高版本的依赖包. 准备工具 ...

  6. Java,图片添加二维码和文字水印,合并为一张图

    在工作中遇到了要把两张图片合并成一张图片的情况,具体场景是用户头像➕二维码➕背景图 本着不重复造轮子的想法,搜索了hutool的utils包,还真让我找到了一个cn.hutool.core.img.I ...

  7. java在底图上生成二维码以及文字

    java在底图上生成二维码以及文字 1.添加依赖 <dependency><groupId>cn.hutool</groupId><artifactId> ...

  8. java生成二维码 推广海报添加二维码 文字水印 二维码添加LOGO

    前言 场景: 一.推广海报贴上二维码,用户扫码跳转             二.二维码中间贴logo   eg:这里使用展示第一种场景 一.使用工具 Google开源项目ZXing(二维条码编解码). ...

  9. Java生成二维码带LOGO底部标题竖版字体

    前言 Java后端生成二维码 底部 侧面带有标题,可调节字号 参考文章 使用Java生成二维码图片(亲测) Reborn_YY使用Java生成二维码图片 图标素材库 Java后台生成图片,前台实现图片 ...

最新文章

  1. thinkphp-比较标签-eq
  2. CentOS 安装Zabbix 手记
  3. PHP 与Python 读取大文件的区别
  4. linux软链接的创建、删除和更新
  5. Cpp 对象模型探索 / 类引入虚函数有哪些成本?
  6. python播放音乐同步歌词_使用Python下载歌词并嵌入歌曲文件中的实现代码
  7. 二进制的认识、进制之间的转换、计算机储存单位
  8. ubuntu 11.10 安装RTX 腾讯通
  9. 高三计算机专业vb试题二答案,高三计算机专业VB试题(二).doc
  10. SQL中EXPLAIN命令详解
  11. 在element框架中使用videojs-markers插件时,无法正常引入的坑
  12. 关于Servlet的两种配置Web.xml文件配置或者使用(@WebServlet(name = ,urlPatterns = ))配置问题——WebServlet注解
  13. 去掉字符串首尾指定字符
  14. 大学生计算机教程个人总结报告,大学生学习自我总结
  15. 【C++】三大易混概念之覆盖
  16. Google圈钱新法:为小网站提供廉价搜索
  17. 4G LTE 频率表
  18. 投资者选择量化基金的四个指标
  19. 分享3款好用的图片工具,你们快来收藏
  20. 许昌学院计算机专业是几本,许昌学院是几本

热门文章

  1. dhcp服务器响应消息有什么,什么是DHCP监听?
  2. 微信小程序幸运大转盘
  3. Nginx核心知识100讲学习笔记(陶辉)Nginx架构基础(一)
  4. Netty系列之Netty百万级推送服务设计要点
  5. 光电转换模块_最近大火的光模块,为什么被5G和云计算双轮驱动?
  6. 步进电机循环脉冲c语言程序,基于C语言的步进电机调速.doc
  7. 附全文下载 |《数字中国指数报告2019》重磅发布!!
  8. 洛谷P1304 哥德巴赫猜想
  9. 浅谈档案数字化建设的意义
  10. 线性可分支持向量机,实例,例题:给定三个数据点:x1=(3,3), x2=(4,3), x3=(1,1),. 其中x1,x2为正例,x3为负例,求线性可分向量机。为什么a1=a3=1/4而不是1/5