FLASK与JAVA的多文件互传(多文件互传亲测)
java:
flask:
# # 安装 Windows 兼容版本 # pip install python-magic-bin # pip install flask
java代码:
package HttpFlask;import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;/*** HttpClient 4.5.14 文件上传工具类*/
public class FileUploader4514 {// 默认配置private static final int DEFAULT_CONNECT_TIMEOUT = 30000; // 30秒private static final int DEFAULT_SOCKET_TIMEOUT = 60000; // 60秒/*** 上传文件到指定URL*/public static UploadResult upload(String url, File file,Map<String, String> textParams) throws IOException {return upload(url, file, file.getName(), textParams, null);}/*** 上传多个文件*/public static UploadResult uploadMultiple(String url, Map<String, File> files,Map<String, String> textParams) throws IOException {try (CloseableHttpClient httpClient = HttpClients.createDefault()) {HttpPost httpPost = new HttpPost(url);MultipartEntityBuilder builder = MultipartEntityBuilder.create();// 添加多个文件if (files != null) {for (Map.Entry<String, File> entry : files.entrySet()) {String fieldName = entry.getKey(); // 表单字段名File file = entry.getValue();FileBody fileBody = new FileBody(file,ContentType.DEFAULT_BINARY, file.getName());builder.addPart(fieldName, fileBody);}}// 添加文本参数if (textParams != null) {textParams.forEach((key, value) ->builder.addTextBody(key, value));}httpPost.setEntity(builder.build());try (CloseableHttpResponse response = httpClient.execute(httpPost)) {int statusCode = response.getStatusLine().getStatusCode();String responseBody = EntityUtils.toString(response.getEntity());return new UploadResult(statusCode, responseBody,statusCode >= 200 && statusCode < 300);}}}//上传多个文件public static UploadResult uploadFilesWithMap(String url,Map<String,File> files)throws IOException {try (CloseableHttpClient httpClient = HttpClients.createDefault()) {HttpPost uploadFile = new HttpPost(url);MultipartEntityBuilder builder = MultipartEntityBuilder.create();// 添加文件,使用不同的字段名for (Map.Entry<String, File> entry : files.entrySet()) {builder.addPart(entry.getKey(), new FileBody(entry.getValue()));}// 添加其他表单参数builder.addPart("category", new StringBody("文档", ContentType.TEXT_PLAIN));builder.addPart("compress", new StringBody("true", ContentType.TEXT_PLAIN));HttpEntity multipart = builder.build();uploadFile.setEntity(multipart);try (CloseableHttpResponse response = httpClient.execute(uploadFile)) {int statusCode = response.getStatusLine().getStatusCode();String responseBody = EntityUtils.toString(response.getEntity());return new UploadResult(statusCode, responseBody,statusCode >= 200 && statusCode < 300);}}}/*** 创建默认请求配置*/private static RequestConfig createDefaultRequestConfig() {return RequestConfig.custom().setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectionRequestTimeout(15000).build();}/*** 创建自定义超时配置*/public static RequestConfig createCustomConfig(int connectTimeout, int socketTimeout) {return RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();}/*** 上传结果封装类*/public static class UploadResult {private final int statusCode;private final String responseBody;private final boolean success;public UploadResult(int statusCode, String responseBody, boolean success) {this.statusCode = statusCode;this.responseBody = responseBody;this.success = success;}public int getStatusCode() { return statusCode; }public String getResponseBody() { return responseBody; }public boolean isSuccess() { return success; }@Overridepublic String toString() {return String.format("UploadResult{statusCode=%d, success=%s, response='%s'}",statusCode, success,responseBody.length() > 100 ? responseBody.substring(0, 100) + "..." : responseBody);}}
}
java测试代码:
package HttpFlask;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class FileHttpFlask {public static void main(String[] args) {try {// 示例4: 多文件上传,flask字典接受Map<String, File> files = new HashMap<>();files.put("file1", new File("E:\\testWorkPlace\\testrun\\src\\testHtt\\down.txt"));files.put("file2", new File("E:\\testWorkPlace\\testrun\\src\\testHtt\\upload.txt"));Map<String, String> parameH = new HashMap<>();parameH.put("category", "huitao");parameH.put("compress", "true");String urlMore="http://127.0.0.1:5000/api/upload-multiple_dict";FileUploader4514.UploadResult multiResult =FileUploader4514.uploadMultiple(urlMore, files, parameH);System.out.println("多文件上传结果dict: " + multiResult);// 示例4: 多文件上传,flask列表接受// 使用不同的字段名(Map的键)Map<String, File> filess = new HashMap<>();String urlList="http://127.0.0.1:5000/api/upload-multiple-filelist";filess.put("file1", new File("E:\\testWorkPlace\\testrun\\src\\testHtt\\down.txt"));filess.put("file2", new File("E:\\testWorkPlace\\testrun\\src\\testHtt\\upload.txt"));filess.put("file3", new File("E:\\testWorkPlace\\testrun\\src\\testHtt\\list.txt"));FileUploader4514.UploadResult multilist = FileUploader4514.uploadFilesWithMap(urlList,filess);System.out.println("多文件上传结果list: " + multilist);} catch (IOException e) {e.printStackTrace();}}
}
flask代码:
from flask import Flask, request, jsonify, render_template_string, send_file
import os
import uuid
from werkzeug.utils import secure_filename
from datetime import datetime
import magic # 用于更准确的文件类型检测
from datetime import datetime
import time
# # 安装 Windows 兼容版本
# pip install python-magic-bin
app = Flask(__name__)# 配置文件上传
app.config.update(UPLOAD_FOLDER='uploads',MAX_CONTENT_LENGTH=16 * 1024 * 1024, # 16MBALLOWED_EXTENSIONS={'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'zip'},SECRET_KEY='your-secret-key-here'
)# 用于存储上传统计信息
upload_stats = {'total_files': 0,'total_size': 0,'last_upload': None
}
# 创建上传目录
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)class FileUploader:"""文件上传工具类"""@staticmethoddef allowed_file(filename):"""检查文件扩展名"""return '.' in filename and \filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']@staticmethoddef generate_unique_filename(original_filename):"""生成唯一文件名"""timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")unique_id = str(uuid.uuid4())[:8]safe_name = secure_filename(original_filename)return f"{timestamp}_{unique_id}_{safe_name}"@staticmethoddef get_file_info(file_path):"""获取文件详细信息"""stat = os.stat(file_path)file_type = "unknown"try:# 使用python-magic检测文件类型import magicfile_type = magic.from_file(file_path, mime=True)except:# 备用方案:使用文件扩展名ext = os.path.splitext(file_path)[1].lower()file_type = f"application/{ext[1:]}" if ext else "unknown"return {'size': stat.st_size,'created_time': datetime.fromtimestamp(stat.st_ctime).isoformat(),'modified_time': datetime.fromtimestamp(stat.st_mtime).isoformat(),'type': file_type}@app.route('/api/upload-multiple_dict', methods=['POST'])
def api_upload_multiple_dict():try:# 将文件转换为字典格式files_dict = request.files.to_dict()if not files_dict:return jsonify({'success': False, 'error': '没有文件部分'}), 400saved_files = []for field_name, file_storage in files_dict.items():if file_storage.filename: # 确保不是空文件filename = secure_filename(file_storage.filename)file_path = os.path.join('uploads', filename)file_storage.save(file_path)saved_files.append({'field_name': field_name,'original_name': file_storage.filename,'saved_name': filename,'size': os.path.getsize(file_path),'content_type': file_storage.content_type})# 处理其他参数category = request.form.get('category', '')compress = request.form.get('compress') == 'true'return jsonify({'success': True,'message': f'成功上传 {len(saved_files)} 个文件','files': saved_files,'category': category,'compress': compress})except Exception as e:return jsonify({'success': False, 'error': str(e)}), 500#多文件上传
@app.route('/api/upload-multiple-filelist', methods=['POST'])
def api_upload_multiple_fileslist():"""接收Java Map<String, File> 格式的多文件上传"""try:# 调试信息:查看请求中的所有文件字段print("请求中的文件字段:", list(request.files.keys()))print("表单参数:", dict(request.form))# 获取所有上传的文件uploaded_files = request.filesif not uploaded_files:return jsonify({'success': False,'error': '没有接收到任何文件'}), 400saved_files = []# 遍历所有文件字段(对应Java Map的键)for field_name, file_storage in uploaded_files.items():# 检查文件是否有效(有文件名且不为空)if file_storage and file_storage.filename and file_storage.filename.strip():# 安全处理文件名original_filename = file_storage.filenamesafe_filename = secure_filename(original_filename)# 如果安全处理后文件名为空,使用原始文件名(去除路径)if not safe_filename:safe_filename = original_filename.split('\\')[-1] # 处理Windows路径safe_filename = safe_filename.split('/')[-1] # 处理Linux路径# 构建保存路径file_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)try:# 保存文件file_storage.save(file_path)# 获取文件大小file_size = os.path.getsize(file_path)saved_files.append({'field_name': field_name, # Java Map中的键名:file1, file2, document'original_name': original_filename,'saved_name': safe_filename,'file_path': file_path,'size': file_size,'content_type': file_storage.content_type or 'application/octet-stream'})print(f"成功保存文件: {field_name} -> {safe_filename} ({file_size} bytes)")except Exception as file_error:print(f"保存文件 {field_name} 时出错: {str(file_error)}")continueelse:print(f"跳过无效文件字段: {field_name}")# 检查是否有文件成功保存if not saved_files:return jsonify({'success': False,'error': '没有有效的文件被上传'}), 400# 获取表单参数(对应Java中的StringBody参数)category = request.form.get('category', 'default')compress = request.form.get('compress', 'false').lower() == 'true'print(f"接收到的参数 - category: {category}, compress: {compress}")# 返回成功响应return jsonify({'success': True,'message': f'成功上传 {len(saved_files)} 个文件','uploaded_files': saved_files,'parameters': {'category': category,'compress': compress},'total_size': sum(f['size'] for f in saved_files)}), 200except Exception as e:print(f"上传处理异常: {str(e)}")return jsonify({'success': False,'error': f'服务器处理错误: {str(e)}'}), 500# 错误处理
@app.errorhandler(413)
def too_large(e):return jsonify({'success': False, 'error': '文件太大'}), 413@app.errorhandler(500)
def internal_error(e):return jsonify({'success': False, 'error': '服务器内部错误'}), 500if __name__ == '__main__':app.run(debug=True, host='0.0.0.0', port=5000)
测试结果: