当前位置: 首页 > news >正文

前端XMLHttpRequest、Fetch API、Axios实现文件上传、下载方法及后端Spring文件服务器处理方法

前言

本文总结Web应用开发中文件上传、下载的方法,即从前端表单输入文件并封装表单数据,然后请求后端服务器的处理过程;从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明,并给出了前端常用的axios库的请求方式,然后给出了后端Spring实现文件服务器的方法,其中就文件上传中遇到的大小限制、跨域请求、外链请求等关键点进行了说明

上传文件

前端上传请求发起的方式:若不依赖其他库常使用XMLHttpRequest、Fetch API上传方式,若使用第三方库可用Axios请求上传

后端文件服务器的几种处理方式:

  • 文保存到磁盘目录

    文件存取请求直接根据url地址得出文件所在磁盘目录位置,访问速度快

  • 文件直接保存到数据库

    若文件比较大,影响数据库处理性能,但相比保存到磁盘目录的方法可以确保数据更安全可靠性

  • 文件二次请求上传至第三方服务器

    后端将接收的表单数据按照第三方接口封装进行请求上传

XMLHttpRequest文件上传

1.表单输入文件

<!-- 设置multiple属性时可选择多个文件,可在读取文件时使用onchange监听读取到文件内容后预览、或立即上传,也可以单独用一个按钮事件控制上传 -->
<input type="file" multiple id="fileInput" onchange="onShowTextContent(event)"></input><pre id="fileContent"></pre>
<button onclick="uploadFile()">上传文件</button>

预览文本内容并显示方法

<script>
function onShowTextContent(event) { // 预览文本内容并显示方法const file = event.target.files[0];if (file && file.type.startsWith("text/plain") ) {const reader = new FileReader();// 定义文件读取完成后的处理逻辑reader.onload = function (e) {const fileContent = e.target.result;// 显示文本内容document.getElementById('fileContent').textContent = fileContent;};// 读取文件为文本内容reader.readAsText(file);} else {console.error('No file selected');}}  
</script>

2.XMLHttpRequest上传文件

XMLHttpRequest对象简记为XHR,一个内置的浏览器对象,提供简单的方式来发送和接收从 URL 的数据而无需刷新整个页面,这使得它成为 AJAX(Asynchronous JavaScript and XML)编程的核心技术之一

AJAX是一种在 Web 应用中通过异步发送 HTTP 请求向服务器获取内容,并使用这些新内容更新页面中相关的部分,而无需重新加载整个页面的 Web 开发技术,这可以让网页更具有响应性,由于这种技术在现代Web开发中太过常见以至于AJAX这个术语很少被使用到

XMLHttpRequest具有广泛的兼容性,几乎所有浏览器都支持,包括老版本浏览器,而Fetch API 但在某些旧浏览器(如 IE)中不支持

function uploadFile() {let fileInput = document.getElementById("fileInput");  // 获得input文件输入的引用let files = fileInput.files;  if (files.length === 0) {return;  // input节点的files是一个文件列表,若 files.length === 0 表示未选择文件,需要跳过}let formData = new FormData(); // 创建表单数据对象,封装文件for (let file of files) {//  file有一些属性常用于校验,如 file.name 文件名称,file.size 文件字节大小,file.type表示文件类型formData.append("files", file); }let request = new XMLHttpRequest();   // 创建一个http请求let url = "http://127.0.0.1:7999/file/upload";request.open("POST", url, true);  // 参数1为请求方式,参数2为上传服务器地址,参数3取true表示异步request.onload = function () {if (request.status === 200) {console.log("上传完成:", request.responseText); } else {console.log("上传出错:", request.statusText);}};request.onerror = function () {console.log("Error:", request.statusText);};request.send(formData); // 发送表单文件数据
}

3. 后端接收文件和保存

先对上传的文件大小、接收文件的类型做判断,然后将原始文件名称、大小、内容类型、存储位置等信息到数据库以提供分类、统计分析

Controller层接收上传的文件
	// @RequestParam 注解标注表单的参数名,与form表单一致,若该注解缺省则默认参数名称为"multipart"// 入参 MultipartFile[] 数组表示接受多个文件,若写为 MultipartFile对象则只会接受第1个文件,cconsumes 指定匹配请求头 "content-type" @PostMapping(value="/upload/list",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)public Map<String,Object> uploadFileList(@RequestParam("files") MultipartFile[] files);
Service层本地存储文件
// controller层调用的文件保存方法,对 MultipartFile[] files 中每个对象处理,判断文件上传个数等
public Map<String,Object> saveMultipartFile(MultipartFile multipartFile) throws IOException {Map<String, Object> results = new HashMap<>();if (multipartFile.getSize() >= 1024 * 1024 * 10) {throw new RuntimeException("文件大小超过了最大限制10M");}// multipartFile.getContentType() 获取内容类型final String oriName = multipartFile.getOriginalFilename(); // 原始文件名不能保证唯一性,需要生成一个唯一文件名,此处仅简单示例,实际开发中文件名称需要确保唯一性String newName = System.currentTimeMillis() + oriName.substring(oriName.lastIndexOf(".")); //String fullPath = System.getProperty("user.dir") + "/" + newName;final InputStream inputStream = multipartFile.getInputStream();File file = new File(fullPath);// 也可以直接使用 multipartFile.transferTo(file);if (file.createNewFile()) {FileCopyUtils.copy(inputStream, Files.newOutputStream(file.toPath()));}// 返回一些有用的信息,若是图片需要返回url等results.put("url","http://127.0.0.1:7999/file/"+newName); return results;}
后端转发请求上传到第三方
    public Result<FileUploadResponse> uploadImageForward(MultipartFile multipartFile) throws IOException {MultiValueMap request = new LinkedMultiValueMap(1);ByteArrayResource byteArrayResource = new ByteArrayResource(multipartFile.getBytes()) {@Override   // 需要覆盖默认的方法,不然无法获取名称public String getFilename() {return multipartFile.getOriginalFilename();}};// 此处从multipartFile获取byte[],如果是上传本地文件可以使用io获取byte[]request.add("fileName", byteArrayResource);String uploadUrl = this.serverBaseUrl+"/upload"; // 假如这是第三方文件上传地址FileUploadResponse response = restTemplate.postForObject(uploadUrl, request, FileUploadResponse.class);  return new Result<>(response);}

4.文件上传大小配置

在 Spring Boot 项目中,可通过配置文件或编程方式来设置文件上传的大小

配置文件方式(application.properties 或 application.yml)

application.properties

# 设置单个文件的最大大小为10MB(默认情况下为1MB)
spring.servlet.multipart.max-file-size=10MB
# 设置整个请求的最大大小为10MB(默认情况下是10MB)
spring.servlet.multipart.max-request-size=10MB

application.yml

spring:servlet:multipart:max-file-size: 10MBmax-request-size: 10MB
编程方式

可进行更细粒度的控制,可以通过编程配置 MultipartConfigElement实现

@Configuration
public class ServletMultipartConfig {// 配置文件中添加示例:spring.server.MaxFileSize=1MB@Value("${spring.server.maxFileSize}")private DataSize maxFileSize;// 指定单个文件最大尺寸,如果超过这个大小,后端将不会接受该请求,前端会手到错误:Code	Details  Error: Request Entity Too Large@Beanpublic MultipartConfigElement multipartConfigElement() {// log.info("MaxFileSize="+maxFileSize.toMegabytes());MultipartConfigFactory factory = new MultipartConfigFactory();factory.setMaxFileSize(maxFileSize); // 但个文件最大大小factory.setMaxRequestSize(maxFileSize); // 整个请求的最大大小return factory.createMultipartConfig();}
}

5.后端跨域请求配置

文件上传在跨域的情况下会上传失败,因此有必要设置服务器的跨域请求规则

springboot的配置类中添加跨域请求配置,以拦截器方式实现为例:

@Configuration
public class WebConfig implements WebMvcConfigurer { @Beanpublic HandlerInterceptor accessInterceptor() {return new HandlerInterceptor(){@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 若请求头设置了Access-Control-Allow-Credentials为true,那"Access-control-Allow-Origin不能使用通配符*,而必须要使用确切的值response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH");// 若前端传送了自定义的请求头而没有在这里面设置,某些情况下则会提示跨域请求错误:not allowed by Access-Control-Allow-Headers in preflight response.response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");response.setHeader("Access-Control-Allow-Credentials", "true"); // 前端请求设置credentials: 'include', 这里就必须设置为trueresponse.setHeader("Access-Control-Max-Age", "3600");// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {response.setStatus(HttpStatus.OK.value());return false;}return true;}};}@Overridepublic void addInterceptors(InterceptorRegistry registry) { // 添加拦截器registry.addInterceptor(this.accessInterceptor()).excludePathPatterns("/error").excludePathPatterns("/webjars/**", "/v2/api-docs/**");}
}

与上述拦截器实现的效果一样,也可以采用过滤器实现跨域请求配置:

   @Bean // 注册过滤器public FilterRegistrationBean<CustomCorsFilter> customCorsFilterFilterRegistrationBean(){FilterRegistrationBean<CustomCorsFilter> registrationBean = new FilterRegistrationBean<>(this.customCorsFilter());registrationBean.setOrder(-1); // 设置序号,越小优先级越高越靠前执行,确保在其他业务处理之前执行到registrationBean.addUrlPatterns("/*");return registrationBean;}@Bean // 确保过滤器被加入到ioc容器public CustomCorsFilter customCorsFilter(){return new CustomCorsFilter();}// 实现一个过滤器,继承Filter接口,覆盖doFilter方法public static class CustomCorsFilter implements Filter{@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) servletResponse;HttpServletRequest request = (HttpServletRequest) servletRequest;response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH");// 若前端传送了自定义的请求头而没有在这里面设置,某些情况下则会提示跨域请求错误:not allowed by Access-Control-Allow-Headers in preflight response.response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");response.setHeader("Access-Control-Allow-Credentials", "true"); // 前端请求设置credentials: 'include', 这里就必须设置为trueresponse.setHeader("Access-Control-Max-Age", "3600"); // 设置 Access-Control-Max-Age 为 3600,表示预检请求的结果可以缓存 3600 秒(即 1 小时),在此期间浏览器不需要为相同的跨域请求再次发送预检请求filterChain.doFilter(servletRequest,servletResponse);}}

Axios文件上传

Axios是一个基于promise的http库,可以用于Node.js中,可兼容所有主流浏览器,该库提供了简洁的API用于执行Http请求,可拦截请求和响应,转换请求和响应请求数据等

引入axios库,然后使用axios提供的方法进行文件上传,后端接收文件的方法与上述一致

<!-- 引入axios库,实际开发中建议用 npm install axios 安装到本地 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function uploadFileByAxios() {const files = document.getElementById('fileInput').files;const formData = new FormData();for (let i = 0; i < files.length; i++) {formData.append('files', files[i]);}let httpHeaders = {};  // 表单Content-Type可以不用设置,默认就是 httpHeaders['Content-Type'] = 'multipart/form-data';// let uploadUrl = "http://127.0.0.1:7999/file/upload";axios.post(uploadUrl, formData, { headers: httpHeaders }) .then(response => {console.log('Files uploaded successfully:', response.data);}).catch(error => {console.error('Error uploading files:', error);});}</script>

Fetch API实现文件上传

Fetch API 是一个用于现代Web应用执行网络请求的内置接口,它在XMLHttpRequest 之后出现

Fetch API 基于 Promise,可以使用链式结构来处理请求和响应,它提供了一种更简单、更灵活的方式来进行异步请求

function uploadFilesByFetch() {let files = document.getElementById('fileInput').files;let formData = new FormData();for (let i = 0; i < files.length; i++) {formData.append('files', files[i]);}let uploadUrl = uploadUrl = "http://127.0.0.1:7999/file/upload";fetch(uploadUrl, {method: 'POST',body: formData}).then(response => {if (!response.ok) {throw new Error('Network response was not ok ' + response.statusText);}return response.json(); }).then(data => {console.log('Files uploaded successfully:', data);}).catch(error => {console.error('Error uploading files:', error);});
}

下载文件

下载文件包括交互事件请求下载、超链接下载

XMLHttpRequest文件下载

1.下载按钮事件

<div><label>下载地址:</label><input type="text" id="downloadUrl" value="http://127.0.0.1:7999/file/download/filename"></input>
</div>
<button onclick="downloadFileByXHR()">XMLHttpRequest下载</button>
<!-- 可以隐藏一个a标签,通过其他按钮事件触发 -->
<button onclick="downloadLink()">超链接下载方式</button><a id="link" style="display: none;"></a>
<!-- 超链接下载方法:直接指定带有href属性的a标签点击即可下载 -->
<a href="http://127.0.0.1:7999/file/download/24071411085841900.txt" download="filename.txt">点击下载文件</a>

2.XMLHttpRequest下载文件

关键是设置responseType,以及对响应数据二进制数据处理

function downloadFileByXHR() {let downloadUrl = document.getElementById("downloadUrl").value; // 下载的url地址// let downloaduUrl = "http://192.168.95.59:7999/download/file/"+fileName;let request = new XMLHttpRequest();request.open("GET", downloadUrl, true);// 响应被处理为 Blob 对象,可以通过 `xhr.response` 访问响应数据,适用于处理二进制数据,如文件、图像等request.responseType = "blob";request.onload = function () {if (request.status === 200) {let contentType = request.getResponseHeader("Content-Type");//验证响应响应数据,若异常则响应 Content-Type 为 json 认定为下载失败if (contentType && contentType.startsWith('application/json')) {const reader = new FileReader();reader.onload = function () {const text = reader.result; // 下载出错时后端返回的错误信息console.log("text:", text);};reader.readAsText(request.response); // 尝试将blob数据读取为文本数据return;}let disposition = request.getResponseHeader("Content-Disposition");// Content-Disposition: attachment; filename=24070701440188300.pngconst data = new Blob([request.response], { type: request.getResponseHeader("Content-Type") });const url = window.URL.createObjectURL(data);const link = document.createElement("a");link.href = url;let fileName = getFileName(disposition);if (fileName !== "") {link.download = fileName;}document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url); // 释放内存link.remove();}};request.onerror = function () {console.low("error:", request.responseText);};request.send();
}

链接下载方法

// 设置隐藏表单下载链接,点击即可下载,这种方法不够友好,若后端错误了前端直接暴露原始错误信息
function downloadLink() {console.log("链接下载");let link = document.getElementById("link");link.href = "http://127.0.0.1:7999/file/download/24071415521018500.txt";// link.download = "test.txt"; // 可指定下载后的名称link.click();
}

3.后端文件下载处理

Controller层接收文件下载请求

	@ApiOperation(value = "下载文件")@RequestMapping(value = "/download/{fileName}", method = RequestMethod.GET)ResponseEntity<byte[]> getDownloadFileResponse(@PathVariable("fileName") String fileName);

Service层读取文件

// 给前端响应一个字节数组,使用ResponseEntity封装以便设置HttpHeaders   
public ResponseEntity<byte[]> getDownloadFileResponse(String fileName) {byte[] imageBytes = this.getLocalFile(fileName); // 获取文件的字节数组String contentType = getFileMediaType(fileName).toString();try {fileName = URLEncoder.encode(fileName, "UTF-8"); // 中文字符串编码处理避免前端收到乱码} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}HttpHeaders headers = new HttpHeaders();// headers.add("Custom-Action", "download");headers.add(HttpHeaders.CONTENT_TYPE, contentType); // 设置Content-Typeheaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); // 设置附件,前端点击超链接下载时该名称将作为下载的文件名称headers.add("Access-Control-Expose-Headers", "Content-Type,Content-Disposition,Custom-Action");return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);}// 读取磁盘指定文件为字节数组public byte[] getLocalFile(String fileName) {File file = new File(localFileProperties.getPath() + "/" + fileName); // 服务器文件位置FileInputStream inputStream = null;try {inputStream = new FileInputStream(file);byte[] bytes = new byte[inputStream.available()];inputStream.read(bytes, 0, inputStream.available());return bytes;} catch (IOException e) {e.printStackTrace();log.error("获取文件失败:",e);throw new RuntimeException("找不到指定的文件");}}// 根据文件名后缀确定图片的 MIME 类型public MediaType getFileMediaType(String imageName) {String fileExtension = imageName.substring(imageName.lastIndexOf(".") + 1).toLowerCase();switch (fileExtension) {case "jpeg":case "jpg":return MediaType.IMAGE_JPEG;case "png":return MediaType.IMAGE_PNG;case "gif":return MediaType.IMAGE_GIF;case "bmp":return MediaType.parseMediaType("image/bmp");default:return MediaType.APPLICATION_OCTET_STREAM; // 默认为二进制流}}

4.后端外链请求方法

Controller层接收一个下载外链的请求

@ApiOperation(value = "第三方外链下载") 
@RequestMapping(value = "/outlink/download", method = RequestMethod.GET)
ResponseEntity<byte[]> getDownloadOutlinkResponse(@RequestParam("url") String url);

Service层读取第三方服务器的文件

这里为啥前端不直接访问第三方文件服务器,因为在一些业务场景中,前端可能没有权限访问直接访问、直接访问不安全或者在对第三方外链的访问情况做分析统计时需要

    public ResponseEntity<byte[]> downloadOutLink(String fileUrl) {log.info("downloadOutLink fileUrl:{}", fileUrl);RestTemplate restTemplate = new RestTemplate();ResponseEntity<byte[]> responseEntity = restTemplate.exchange(fileUrl, HttpMethod.GET, null, byte[].class);byte[] bytes = responseEntity.getBody();log.info("return entity...");HttpHeaders oldHeaders = responseEntity.getHeaders();oldHeaders.entrySet().forEach(entry -> {log.info("headName:{}, headValue:{}", entry.getKey(), entry.getValue());});String contentDisposition = oldHeaders.getFirst(HttpHeaders.CONTENT_DISPOSITION);String contentType = oldHeaders.getFirst(HttpHeaders.CONTENT_TYPE);HttpHeaders responseHeaders = new HttpHeaders(); //  新创建一个 responseHeaders 用于设置返回前端的请求头,此处没有直接使用oldHeaders,因为这样可能会暴露出现某些不被允许的请求头以至于出错if (contentDisposition != null && !Objects.equals("", contentDisposition)) {responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);} else {// 获取一个文件扩展名String fileSuffix = "";if(fileUrl.contains(".")){String suffix = fileUrl.substring(fileUrl.lastIndexOf(".")+1).toLowerCase();if(suffix.length()>0  && suffix.length()<5){fileSuffix = suffix;}}if(Objects.equals(fileSuffix,"")){fileSuffix = "download";}String fileName = System.currentTimeMillis()+"."+fileSuffix;responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s",fileName));}responseHeaders.add(HttpHeaders.CONTENT_TYPE, contentType);return ResponseEntity.ok().headers(responseHeaders).body(bytes);}

Fetch API下载文件

function downloadFileByFetch() {// const url = 'http://127.0.0.1:7999/file/upload'; // 文件的URLlet url = document.getElementById("downloadUrl").value;fetch(url).then(async (response) => {console.log("response:", response);if (!response.ok) {throw new Error('Network response was not ok ' + response.statusText);}const contentType = response.headers.get('Content-Type');// 后端抛出异常时,返回json数据if(contentType && contentType.startsWith("application/json")){const jsonResponse = await response.json();if (jsonResponse.code === 1) {// 后端返回了统一错误响应console.error('Request failed with message:', jsonResponse.msg);showInfo(JSON.stringify(jsonResponse));return;}}console.log("contentType:",contentType);const disposition = response.headers.get('Content-Disposition');console.log("disposition:", disposition);let fileName = "unknown.download";if (disposition !== null) {const fileNameMatch = disposition.match(/filename="?([^"]+)"?/);if (fileNameMatch.length === 2) {fileName = fileNameMatch[1];}fileName = fileName.replace(/\+/g, ' ');fileName = decodeURI(fileName);}response.blob().then(blob => {const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = fileName; // 设置下载文件名document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url); // 释放URL对象});}).catch(error => {console.error('error:', error);});
}

Axios下载文件

const downloadFileByAxios = () => {// 使用 Axios 发起 GET 请求// let url = "http://192.168.95.59:7999/download/file/xxx";let url = document.getElementById("downloadUrl").value;let httpHeaders = {};// httpHeaders = { "Custom-Action": "RQ-download" };axios.get(url, { responseType: 'blob', headers: httpHeaders }).then(response => {// 请求成功处理console.log("收到下载数据:", response);// let res = response.data;handleAxiosDownloadResponse(response.data, response.headers);}).catch(error => {// 请求失败处理console.error('Error fetching posts:', error);});
}    // 处理axios下载的响应文件
const handleAxiosDownloadResponse = async (data, headers) => {const contentType = headers['content-type'];// 后端返回的不是文件而是json对象,判断为异常if (contentType && contentType.startsWith("application/json")) {// data.text()返回一个Promise对象,因此要使用await,此时方法使用使用async关键字标识为异步方法let text = await data.text();console.log("text:", text);const jsonObj = JSON.parse(text);if (jsonObj.code === 1) {console.log("请求异常:", text);}showInfo(text);return;}const blob = new Blob([data], { type: headers['content-type'] });// 创建一个指向 Blob 对象的 URLconst url = window.URL.createObjectURL(blob);// 创建一个 <a> 元素,并设置其 href 属性指向 Blob URLconst a = document.createElement('a');a.href = url;// 从响应头或其他方式获取文件名const contentDisposition = headers['content-disposition'];console.log("content-disposition:", contentDisposition)let fileName = '';if (contentDisposition) {const fileNameMatch = contentDisposition.match(/filename="?([^"]+)"?/);if (fileNameMatch.length === 2) {fileName = fileNameMatch[1];}fileName = fileName.replace(/\+/g, ' ');  // 将符号+替换为空格fileName = decodeURI(fileName);// console.log("fileName:",fileName);}a.download = fileName; // 设置下载文件名// 将 <a> 元素添加到 DOM 并触发点击事件document.body.appendChild(a);a.click();// 移除 <a> 元素并释放 Blob URLdocument.body.removeChild(a);window.URL.revokeObjectURL(url);
}

Axios读取文件内容

const readFileContentByAxios = () => {// 使用 Axios 发起 GET 请求// let url = "http://192.168.95.59:7999/download/file/xxx";let url = document.getElementById("downloadUrl").value;let httpHeaders = {};// responseType选项取值与XMLHttpRequest的responseType一样,指定为arraybuffer,表示响应被处理为 ArrayBuffer 对象,适用于处理原始二进制数据axios.get(url, { responseType: 'arraybuffer', headers: httpHeaders }).then(async (response) => {// 请求成功处理console.log("收到数据:", response);// let res = response.data;const text = new TextDecoder('utf-8').decode(new Uint8Array(response.data));console.log(text); }).catch(error => {// 请求失败处理console.error('Error fetching posts:', error);});
}

参考资料

1.XMLHttpRequest(XHR)对象

2.Fetch API接口指南

3.Axios API中文文档

4.Spring Framework文档

相关文章:

前端XMLHttpRequest、Fetch API、Axios实现文件上传、下载方法及后端Spring文件服务器处理方法

前言 本文总结Web应用开发中文件上传、下载的方法&#xff0c;即从前端表单输入文件并封装表单数据&#xff0c;然后请求后端服务器的处理过程&#xff1b;从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明&#xff0c;并给出了前端常用的axios库的请…...

STM32智能交通监测系统教程

目录 引言环境准备智能交通监测系统基础代码实现&#xff1a;实现智能交通监测系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;交通监测与管理问题解决方案与优化收尾与总结 1. 引言 智能交通监测系统通…...

【利用Selenium+autoIt实现文件上传】

利用Selenium+autoIt实现文件上传 利用Selenium+autoIT实现文件上传autoIt脚本制作转换成exe文件java代码运行部分利用Selenium+autoIT实现文件上传 当你看到这篇文章时,证明你遇到了和我一样的难题。正常情况下我们利用selenium完全可以实现表单的提交和文件上传等操作。但当…...

python join

1、join函数 *.join(seq) 以*作为分隔符&#xff0c;将seq所有的元素合并为一个新的字符串 seq ABDWDPO new_seq list(.joint(seq)) # ABDWDPO #[A, B, D, W, D, P, O]...

cython加速python代码

python这个语言在使用的层面上看几乎没有缺点&#xff0c;简单易学&#xff0c;语法简单&#xff0c;唯一的弱点就是慢&#xff0c; 当然了万能的python社区是给了解决方法的&#xff0c;那就是cython 使用Cython可以显著提升Python代码的执行效率&#xff0c;特别是在涉及到数…...

React@16.x(60)Redux@4.x(9)- 实现 applyMiddleware

目录 1&#xff0c;applyMiddleware 原理2&#xff0c;实现2.1&#xff0c;applyMiddleware2.1.1&#xff0c;compose 方法2.1.2&#xff0c;applyMiddleware 2.2&#xff0c;修改 createStore 接上篇文章&#xff1a;Redux中间件介绍。 1&#xff0c;applyMiddleware 原理 R…...

level 6 day1 Linux网络编程之网络基础

v1 网络的历史和分层 TCP 是可靠传输&#xff0c;IP协议是不可靠传输 网络的体系结构 网络分层的思想&#xff1a; OSI体系结构 两层交换机是指数据链路层的交换 三层交换是指网络层这边的交换 四层模型 蓝色的字 是由手机发给PC机&#xff0c;由传输层来决定应该交给哪一…...

PostgreSQL UPDATE 命令

PostgreSQL UPDATE 命令 PostgreSQL 是一种功能强大的开源对象关系型数据库管理系统&#xff08;ORDBMS&#xff09;&#xff0c;它使用并扩展了SQL语言。在处理数据库时&#xff0c;我们经常需要更新现有的记录。在PostgreSQL中&#xff0c;UPDATE命令用于修改表中的现有记录…...

什么? CSS 将支持 if() 函数了?

CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见&#xff1a;css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候&#xff0c;心中直呼这很逆天了&#xff0c;我们知道像 less 这些 css 这些预…...

function calling实现调用理杏仁api获取数据

LLM是不存在真正逻辑的且并不是知晓万事万物的&#xff08;至少目前是这样&#xff09;在很多更垂直的环境下LLM并不能很好的赋能。 function calling的实现使LLM可以对接真正的世界以及真正有逻辑的系统&#xff0c;这将很大程度上改变LLM的可用范围&#xff08;当然安全问题依…...

Excel中用VBA实现Outlook发送当前工作簿

Excel中用VBA实现Outlook发送当前工作簿&#xff0c;首先按AltF11打开VBA编辑器&#xff0c;插入模块&#xff0c;并在工具-引用中勾选 Microseft Outlook .0 Object Library(其中为你Microseft Outlook的版本号。 Sub 发送邮件() 保存当前excel ThisWorkbook.Save让excel连接…...

从 ArcMap 迁移到 ArcGIS Pro

许多 ArcMap 用户正在因 ArcGIS Pro 所具有的现代 GIS 桌面工作流优势而向其迁移。 ArcGIS Pro 与其余 ArcGIS 平台紧密集成&#xff0c;使您可以更有效地共享和使用内容。 它还将 2D 和 3D 组合到一个应用程序中&#xff0c;使您可以在同一工程中使用多个地图和多个布局。 Arc…...

WSL2 的安装与运行 Linux 系统

前言 适用于 Linux 的 Windows 子系统 (WSL) 是 Windows 的一项功能&#xff0c;允许开发人员在 Windows 系统上直接安装并使用 Linux 发行版。不用进行任何修改&#xff0c;也无需承担传统虚拟机或双启动设置的开销。 可以将 WSL 看作也是一个虚拟机&#xff0c;但是它更为便…...

业务终端动态分配IP-DHCP技术、DHCP中继技术

一、为什么需要DHCP? 1、许多设备(主机、无线WiFi终端等)需要动态地址的分配; 2、人工手工配置任务繁琐、容易出错,比如:IP地址冲突; 3、网络规模扩大、复杂度提高,网络配置越来越复杂,计算机的位置变化和数量超过可分配IP地址的数量,造成IP地址变法频繁以及IP地址…...

新一代大语言模型 GPT-5 对工作与生活的影响及应对策略

文章目录 &#x1f4d2;一、引言 &#x1f4d2;二、GPT-5 的发展背景 &#x1f680;&#xff08;一&#xff09;GPT-4 的表现与特点 &#x1f680;&#xff08;二&#xff09;GPT-5 的预期进步 &#x1f4d2;三、GPT-5 对工作的影响 &#x1f680;&#xff08;一&#xf…...

AI基于大模型语言存在的网络安全风险

目的&#xff1a; 随着大语言模型&#xff08;LLM&#xff09;各领域的广泛应用&#xff0c;我们迫切需要了解其中潜在的风险和威胁&#xff0c;及时进行有效的防御。 申明&#xff1a; AI技术的普及正当的使用大模型技术带来的便利&#xff0c;切勿使用与非法用途&#xff…...

探索Perl语言:入门学习与实战指南

文章目录 探索Perl语言&#xff1a;入门学习与实战指南一、Perl语言概述二、Perl的安装与配置安装PerlWindowsmacOSLinux 配置Perl 三、基本语法与数据类型标量变量数组哈希 四、控制结构条件语句循环语句 五、子程序与模块子程序模块 六、文件操作与正则表达式文件读取与写入正…...

dp or 数学问题

看一下数据量&#xff0c;只有一千&#xff0c;说明这个不是数学问题 #include<bits/stdc.h> using namespace std;#define int long long const int mo 100000007; int n, s, a, b; const int N 1005;// 2 -3 // 1 3 5 2 -1 // 1 -2 -5 -3 -1 int dp[N][N]; int fun…...

kibana连接elasticsearch(版本8.11.3)

前言 elasticsearch在8版本之后就出现了很大变化&#xff0c;由于kibana版本需要需elasticsearch进行版本对象&#xff0c;kibana连接方式也出现了很大变化。我在这里记录下自己的踩坑记录。 服务部署 本文中的服务都是在docker环境中部署的。其中elasticsearch版本和kibana版…...

基于python的图像去水印

1 代码 import cv2 import numpy as npdef remove_watermark(image_path, output_path):# 读取图片image cv2.imread(image_path)# 转换为灰度图gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用中值滤波去除噪声median_filtered cv2.medianBlur(gray, 5)# 计算图像的梯…...

Linux下Supervisor的安装与配置

软件工程中,守护进程是非常行之有效的方案。能够让我们的一些指令在崩溃之后可以自我重新启动,从而保障业务上的持续使用。 这里就从0开始教大家安装Supervisor。 一,下载安装 安装有好多种方法,直接下载安装包安装,或者yum安装或者pip安装都可以。这次我们选择的是pip…...

使用Pandas读取Excel文件将特定列转成str格式方法汇总

文章目录 读取Excel文件并确保列为字符串类型使用 dtype 参数使用 converters 参数 读取Excel文件的正确拼写示例&#xff1a;读取Excel文件并过滤包含特定值的行详细解释 读取Excel文件并确保列为字符串类型 正确的方法是使用 pd.read_excel 函数&#xff0c;并指定 dtype 或…...

FPGA CFGBVS 管脚接法

说明 新设计了1个KU040 FPGA板子&#xff0c;回来之后接上JTAG FPGA不识别。做如下检查&#xff1a; 1、电源测试点均正常&#xff1b; 2、查看贴片是否有漏焊&#xff0c;检查无异常&#xff0c;设计上NC的才NC&#xff1b; 3、反复检查JTAG接线是否异常&#xff0c;贴片是…...

快速排序及归并排序的实现与排序的稳定性

目录 快速排序 一. 快速排序递归的实现方法 1. 左右指针法 步骤思路 为什么要让end先走&#xff1f; 2. 挖坑法 步骤思路 3. 前后指针法 步骤思路 二. 快速排序的时间和空间复杂度 1. 时间复杂度 2. 空间复杂度 三. 快速排序的优化方法 1. 三数取中优化 2. 小区…...

【系统架构设计】数据库系统(一)

数据库系统&#xff08;一&#xff09; 数据库模式与范式数据库的结构与模式数据模型关系代数数据的规范化反规范化 数据库设计事务管理备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库的结构与模式 数据库技术中采用分级的方法将数据库的结…...

泛微e-cology WorkflowServiceXml SQL注入漏洞(POC)

漏洞描述&#xff1a; 泛微 e-cology 是泛微公司开发的协同管理应用平台。泛微 e-cology v10.64.1的/services/接口默认对内网暴露&#xff0c;用于服务调用&#xff0c;未经身份认证的攻击者可向 /services/WorkflowServiceXml 接口发送恶意的SOAP请求进行SQL注入&#xff0c;…...

<Rust><GUI>rust语言GUI库tauri体验:前、后端结合创建一个窗口并修改其样式

前言 本文是rust语言下的GUI库&#xff1a;tauri来创建一个窗口的简单演示&#xff0c;主要说明一下&#xff0c;使用tauri这个库如何创建GUI以及如何添加部件、如何编写逻辑、如何修改风格等&#xff0c;所以&#xff0c;这也是一个专栏&#xff0c;将包括tauri库的多个方面。…...

OBD诊断(ISO15031) 09服务

文章目录 功能简介ISO 9141-2、ISO 14230-4和SAE J1850的诊断服务定义1、请求车辆信息请求消息&#xff08;读取支持的INFOTYPE&#xff09;2、请求车辆信息响应消息(报告支持INFOTYPE)3、请求车辆信息请求消息&#xff08;读取INFOTYPE值&#xff09;4、请求车辆信息响应消息&…...

客户端与服务端之间的通信连接

目录 那什么是Socket? 什么是ServerSocket? 代码展示&#xff1a; 代码解析&#xff1a; 补充&#xff1a; 输入流&#xff08;InputStream&#xff09;&#xff1a; 输出流&#xff08;OutputStream&#xff09;&#xff1a; BufferedReader 是如何提高读取效率的&a…...

Font Awesome 图表图标

Font Awesome 图表图标 Font Awesome 是一个广泛使用的图标库&#xff0c;它提供了大量的图标&#xff0c;可以轻松地用于网页设计和开发中。在本文中&#xff0c;我们将重点介绍 Font Awesome 中的图表图标&#xff0c;探讨它们的特点、使用方法&#xff0c;并展示一些实际的…...