目录指引
背景
本人,去年毕业的Java后端蒟蒻,目前就职于一个没有正经前端开发的公司。
前段时间公司项目中需要在页面上实现一个二维码下载的按钮,之前从没接触过前端,从零开始熟悉,磕磕绊绊实现了功能,为了以后方便复用,因此作一个记录。
运用场景
在前端展示一个按钮,动态获取地址并生成对应地址的二维码,用户点击下载二维码图片到用户电脑本地。
环境
- Vue 2.6.11
- axios 0.18.1
- JDK 1.8.0_321
- Maven 3.8.4
丑话说在前
本文最终实现方法是在部署服务器上先生成一个二维码图片,再拉取到用户电脑上,因此最好需要在部署服务器中创建一个文件夹来存放二维码图片并定期处理。
应该是可以通过直接传输流的方式避免这个问题,但当时实现的时候并没有成功,有机会再优化。
前端部分
通过axios请求传参
二次封装axios.create方法
这边api基础地址写的是本地,可以改成部署服务器地址
import axios from 'axios'
const service = axios.create({
baseURL: "http://localhost:8080", //api基础地址
timeout: 9000 //请求超时时间
})
定义download方法,通过axios进行传参
这边import指向的是前面封装axios方法的js文件
import { axios } from '@/utils/request'
export function downFile(url,parameter){
return axios({
url: url,
params: parameter,
method:'get' ,
responseType: 'blob'
})
}
前端vue页面方法
HTML部分按键定义
定义一个a标签,添加onclick事件,指向下面js部分的下载二维码方法
<a onclick="generateQRcode(QRCodeUrl)">下载二维码</a>
js部分调用方法
这边import指向的是前面传参部分定义js方法的文件
import {downFile} from "@api/manage";
在data中定义生成二维码的api地址
url: {
generateQRcode: "example/generateQRcode",
}
在methods中定义下载二维码方法
这边是从后端接收文件流,转换为blob对象后进行输出下载
generateQRcode(QRCodeUrl){
downFile(this.url.generateQRcode,{QRCodeUrl: QRCodeUrl}).then((data)=>{
if (!data) {
this.$message.warning("文件下载失败")
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data],{type: 'image/jpeg'}), QRCode+'.jpg')
}else{
let url = window.URL.createObjectURL(new Blob([data],{type: 'image/jpeg'}))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', 'QRCode.jpg')
document.body.appendChild(link)
link.click()
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
}
})
},
后端部分
依赖导入
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.1</version>
</dependency>
控制层
这边要生成二维码的网址就是前端传过来的参数,可以通过各种方法进行传参动态修改
@RequestMapping("/example")
public class ExampleController {
@RequestMapping(value = "/generateQRcode", method = RequestMethod.GET)
public void generateQRcode(@RequestParam(name = "QRCodeUrl", required = true) String QRCodeUrl, HttpServletRequest request,HttpServletResponse response) throws IOException {
ExampleService.generateQRcode(QRCodeUrl, request, response);
}
服务层
public interface ExampleService {
void generateQRcode(String QRCodeUrl, HttpServletRequest request, HttpServletResponse response) throws IOException;
}
服务实现类
@Service
public class ExampleServiceImpl implements ExampleService {
@Override
public void generateQRcode(String QRCodeUrl, HttpServletRequest request, HttpServletResponse response) throws IOException {
//生成二维码
//指定生成路径
String fileName = "QRcode";
//保存在服务器上的路径
String path = FileSystemView.getFileSystemView().getHomeDirectory() + File.separator + "QRCode" + File.separator;
//生成二维码
QRCodeUtils.createCode(QRCodeUrl, path, fileName + ".jpg");
//下载二维码
QRCodeUtils.downFile(path, fileName + ".jpg", request , response);
}
}
生成二维码工具类
public class QRCodeUtils {
/**
* 生成二维码并下载
*
* @param url 二维码的内容
* @param path 二维码保存路径
* @param fileName 二维码保存名称
* @return BitMatrix对象
*/
public static void createCode(String url, String path, String fileName) throws IOException {
//其他参数,如字符集编码
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//容错级别为H
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//白边的宽度,可取0~4
hints.put(EncodeHintType.MARGIN, 0);
BitMatrix bitMatrix = null;
try {
bitMatrix = new MultiFormatWriter().encode(url,
BarcodeFormat.QR_CODE, 256, 256, hints); //解码url,设置二维码宽高256px
File file = new File(path, fileName); //设置二维码路径&名字
writeToFile(bitMatrix, "jpg", file); //下载二维码,格式jpg
} catch (WriterException e) {
e.printStackTrace();
}
}
/**
* 二维码下载方法
* @param url
* @param fileName
* @param request
* @param response
*/
public static void downFile(String url, String fileName,HttpServletRequest request, HttpServletResponse response) {
try {
//1.定义ContentType为("multipart/form-data")让浏览器自己解析文件格式
response.setContentType("multipart/form-data");
//2.中文名转码
//response.setHeader("Content-disposition", "attachment; filename=\""+encodeChineseDownloadFileName(request, fileName+".xlsx") +"\"");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
//获得文件
File file = new File(url+fileName);
FileInputStream in = new FileInputStream(file);
//3.将文件写入缓冲区OutputStream(out)
OutputStream out = new BufferedOutputStream(response.getOutputStream());
int b = 0;
byte[] buffer = new byte[2048];
while ((b=in.read(buffer)) != -1){
//4.将缓冲区文件输出到客户端(out)
out.write(buffer,0,b);
}
in.close();
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 二维码写入文件方法
*
* @param matrix 二维码BitMatrix对象
* @param format 下载图片格式
* @param file 二维码文件对象,包含文件路径&名称
* @throws IOException
*/
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);
}
}
}
小结
至此,当用户点击网页中下载二维码按钮,一张名为QRCode.jpg的图片将下载到用户本地。
不需要下载到服务器的方法已经*// TODO*了,有时间再优化(doge)。
也欢迎各位路过的大犇指正问题~