JMeter分块上传文件:能否用内存文件变量替代本地文件路径?
JMeter分块上传文件:如何用内存文件替代物理文件路径?
问题描述
我正在编写JMeter测试计划,实现分块向服务器上传文件,但Java知识有限。目前我结合While控制器和BeanShell预处理器,写了从文件读取字节块的脚本,但遇到一个问题:HTTP请求采样器的文件上传区域要求填写物理文件路径,我想知道能不能在BeanShell预处理器中创建内存文件,然后把这个内存文件的变量用到文件路径字段里?
我觉得理论上是可行的,毕竟文件上传时本来也是先加载到内存再发送给服务器。有没有办法从1MB大小的字节块创建内存文件,然后作为上传文件使用?
附上我当前的BeanShell预处理器代码:
Integer maxChunkSize = new Integer(vars.get("FILE_MAX_SIZE")); String uploadFilePath = vars.get("UPLOAD_FILE"); uploadFileSize = new File(uploadFilePath).length(); InputStream uploadFile = new BufferedInputStream(new FileInputStream(uploadFilePath)); int offset = whileCounter * maxChunkSize; ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] bytes; int size; /* creating bytes array to hold file data*/ if (offset < uploadFileSize) { if (((int) offset + (int) maxChunkSize) < uploadFileSize) { bytes = new byte[ (int) maxChunkSize]; size = maxChunkSize; } else { size = (int) (uploadFileSize - offset); bytes = new byte[ (int) size]; vars.put("WHILE_LOOP", "0"); } } /* printing results for debugging */ /* log.info(" ============================================================== "); log.info("While counter " + whileCounter.toString() ); log.info("While loop " + vars.get("WHILE_LOOP").toString() ); log.info("The file to upload is : " + uploadFilePath); log.info("Maximum Chunk size is : " + maxChunkSize.toString()); log.info("Current Offset is : " + offset.toString()); log.info("The file size is " + uploadFileSize.toString()); log.info(" ============================================================== "); */ /* here it is giving method invocation on loop counter 2, so skip method is used */ uploadFile.skip(offset); int bytesRead = uploadFile.read(bytes, 0, size); /* write to byte output stream to read as a file */ bos.write(bytes, 0, bytesRead); /* params for next iteration */ uploadFile.close(); whileCounter++; vars.put("WHILE_COUNTER", whileCounter.toString() );
我需要两种方向的帮助:要么是JMeter分块上传文件的替代方案,要么是实现能在HTTP请求采样器文件上传区域使用的内存文件变量的方法。
解决方案
方案一:用JSR223预处理器直接构造Multipart请求(推荐)
HTTP请求采样器的文件上传字段确实只认物理文件路径,但我们可以绕过这个限制——直接在JSR223预处理器中构造完整的Multipart/form-data请求体,完全替代采样器的自动参数处理。
具体步骤:
- 配置HTTP请求采样器:
- 请求方法设为
POST - 切换到「高级」标签页,勾选「使用multipart/form-data」和「禁用自动编码」
- 不要填写文件上传区域的任何内容
- 请求方法设为
- 添加JSR223预处理器,选择Groovy语言(性能远优于BeanShell),写入以下脚本:
def maxChunkSize = Integer.parseInt(vars.get("FILE_MAX_SIZE")) def uploadFilePath = vars.get("UPLOAD_FILE") def uploadFile = new File(uploadFilePath) def fileSize = uploadFile.length() def whileCounter = Integer.parseInt(vars.getOrDefault("WHILE_COUNTER", "0")) def offset = whileCounter * maxChunkSize // 计算当前块的实际大小 def chunkSize = (offset + maxChunkSize) < fileSize ? maxChunkSize : (fileSize - offset) if (chunkSize <= 0) { vars.put("WHILE_LOOP", "0") return } // 读取当前块的字节数据 def bytes = new byte[chunkSize as int] def inputStream = new BufferedInputStream(new FileInputStream(uploadFile)) inputStream.skip(offset) inputStream.read(bytes, 0, chunkSize as int) inputStream.close() // 生成Multipart边界(避免与内容冲突) def boundary = "----WebKitFormBoundary${UUID.randomUUID()}" sampler.getArguments().clear() // 拼接请求体头部 def headerPart = new StringBuilder() headerPart.append("--").append(boundary).append("\r\n") headerPart.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(uploadFile.getName()).append("\"\r\n") headerPart.append("Content-Type: application/octet-stream\r\n\r\n") // 将头部、字节数据、尾部加入请求参数 sampler.getArguments().addArgument("", headerPart.toString(), null) sampler.getArguments().addArgument("", new String(bytes, "ISO-8859-1"), null) sampler.getArguments().addArgument("", "\r\n--" + boundary + "--\r\n", null) // 设置请求头的Content-Type sampler.getHeaderManager().add("Content-Type", "multipart/form-data; boundary=" + boundary) // 更新循环计数器 whileCounter++ vars.put("WHILE_COUNTER", whileCounter.toString())
- 配置While控制器的条件为
${WHILE_LOOP} == "1"(测试前记得初始化WHILE_LOOP变量为"1")
方案二:临时文件替代内存文件(简单易上手)
如果不想直接构造请求体,也可以把读取到的字节块写入临时文件,再把临时文件路径传给HTTP请求采样器。
修改你的BeanShell脚本,在末尾添加写入临时文件的代码:
// 保留你原来的所有代码... // 将字节流写入临时文件 File tempFile = File.createTempFile("upload_chunk_", ".tmp"); FileOutputStream fos = new FileOutputStream(tempFile); bos.writeTo(fos); fos.close(); bos.close(); // 把临时文件路径存入变量,供HTTP请求采样器使用 vars.put("CHUNK_FILE_PATH", tempFile.getAbsolutePath()); // 可选:添加TearDown线程组,在测试结束后清理临时文件 // 可以在TearDown中执行 new File(vars.get("CHUNK_FILE_PATH")).delete();
之后在HTTP请求采样器的文件上传区域,直接填写${CHUNK_FILE_PATH}作为文件路径即可。
额外提示
- 优先用JSR223+Groovy替代BeanShell:Groovy性能更高,语法更灵活,对Java新特性支持更好。
- 分块上传时记得给服务器传递分块标识:比如添加
Content-Range请求头,或者自定义Chunk-Index、Total-Chunks等字段,否则服务器无法拼接完整文件。 - 添加响应断言:验证每块上传后的服务器返回结果,避免出现丢块或上传失败的情况。
内容的提问来源于stack exchange,提问作者shantanu sharma
相关产品推荐
相关产品推荐

