上传文件
一、自定义上传文件
1 from flask import Flask,request
2 from datetime import datetime
3 import random
4 app = Flask(__name__)
5
6 @app.route('/upload',methods=['POST'])
7 def upload():
8 data = request.files.get('pic')
9 # 随机图片名称
10 pic=datetime.now().strftime('%Y%m%d%H%M%S')+str(random.randint(1000,9999))+'.png'
11 with open(pic,'wb') as f:
12 f.write(data.read())
13 return ''
14
15 if __name__ == '__main__':
16 app.run(debug=True)
<form action='http://127.0.0.1:5000/upload' method='post' enctype="multipart/form-data"> <input type='file' name='pic' /> <input type='submit' value='submit' /> </form>
二、第三方上传文件 (webuploader)
1 # server.py
2 # 引入os模块,1,删除分片,节约物理空间,2,遍历上传的文件
3 import os
4 from flask import Flask, request, Response, render_template,jsonify
5 app = Flask(__name__)
6 app.config.from_object('config')
7
8 # 进入上传页面
9 @app.route('/')
10 def index():
11 return render_template('uploads.html')
12
13 # 检查上传分片是否重复,如果重复则不提交,否则提交
14 @app.route('/checkChunk', methods=['POST'])
15 def checkChunk():
16 chunkSize = request.form.get('chunkSize') # 待上传文件的大小
17 if chunkSize == '0':
18 return jsonify({'ifExist': True}) # 不要上传
19 file_name = request.form.get('fileMd5')+ request.form.get('chunk')
20 if file_name not in get_deep_catalog():
21 return jsonify({'ifExist': False})
22 return jsonify({'ifExist':True})
23
24 # 定义一个获取当前路径下的所有文件,应该放在公共函数库中
25 def get_deep_catalog(path='./upload'):
26 result = []
27 list_catalog = os.listdir(path)
28 for i in list_catalog:
29 if os.path.isdir(i) == True:
30 get_deep_catalog(i)
31 else:
32 result.append(i)
33 return result
34
35 # 将每次上传的分片合并成一个新文件
36 @app.route('/mergeChunks', methods=['POST'])
37 def mergeChunks():
38 fileName=request.form.get('fileName') # 上传的文件名
39 md5=request.form.get('fileMd5')
40 chunk = 0 # 分片序号
41 with open(u'./upload/{}'.format(fileName), 'wb') as target_file: # 创建新文件
42 while True:
43 try:
44 filename = './upload/{}-{}'.format(md5, chunk)
45 source_file = open(filename, 'rb') # 按序打开每个分片
46 target_file.write(source_file.read()) # 读取分片内容写入新文件
47 source_file.close()
48 except Exception as e:
49 break
50 chunk += 1
51 os.remove(filename) # 删除该分片,节约空间
52 return jsonify({'upload':True,'fileName':fileName})
53
54
55 # 前端上传的分片 保存到 指定的目录下 ./upload
56 @app.route('/upload', methods=['POST'])
57 def upload(): # 接收前端上传的一个分片
58 md5=request.form.get('fileMd5')
59 chunk_id=request.form.get('chunk',0,type=int)
60 filename = '{}-{}'.format(md5,chunk_id)
61 upload_file = request.files['file']
62 upload_file.save('./upload/{}'.format(filename))
63 return jsonify({'upload_part':True})
64
65
66 # 遍历 upload下的上传文件
67 @app.route('/file/list', methods=['GET'])
68 def file_list():
69 files = os.listdir('./upload/') # 获取文件目录
70 return render_template('list.html', files=files)
71
72 # 文件下载
73 @app.route('/file/download/<filename>', methods=['GET'])
74 def file_download(filename):
75 def send_chunk(): # 流式读取
76 store_path = './upload/%s' % filename
77 with open(store_path, 'rb') as target_file:
78 while True:
79 chunk = target_file.read(20 * 1024 * 1024)
80 if not chunk:
81 break
82 yield chunk
83 # application/octet-stream 是二进制文件的数据流或者字节数组
84 return Response(send_chunk(), content_type='application/octet-stream')
85
86 if __name__ == '__main__':
87 app.run(debug=True)
1 <!--
2 上传页面 uploads.html
3 extensions : 上传数据类型 例如 mp4
4 mimeTypes : 媒体资源类型,如 .mp4
5 -->
6 <!DOCTYPE html>
7 <html>
8 <head>
9 <meta charset="UTF-8">
10 <title>webuploader上传</title>
11 <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.1/webuploader.css">
12 <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
13 <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
14 <script src="/static/js/webuploader.min.js"></script>
15 <script type="text/javascript" src="/static/js/hashmap.js"></script>
16 <style type="text/css">
17 #picker {
18 display: inline-block;
19 line-height: 1.428571429;
20 vertical-align: middle;
21 margin: 0 12px 0 0;
22 }
23 </style>
24 </head>
25 <body>
26 <div id="uploader" class="container">
27 <!--用来存放文件信息-->
28 <div id="thelist" class="row">
29 <div class="panel panel-primary">
30 <div class="panel-heading">webuploader文件上传</div>
31 <table class="table table-striped table-bordered" id="uploadTable">
32 <thead>
33 <tr>
34 <th>序号</th>
35 <th>文件名称</th>
36 <th>文件大小</th>
37 <th>上传状态</th>
38 <th>上传进度</th>
39 <th>操作</th>
40 </tr>
41 </thead>
42 <tbody></tbody>
43 </table>
44 <div class="panel-footer">
45 <div id="picker">选择文件</div>
46 <button id="btn" class="btn btn-default">开始上传</button>
47 </div>
48 </div>
49 </div>
50 </div>
51 <script type="text/javascript">
52 var fileMd5;
53 var fileSuffix;
54 var $list=$("#thelist table>tbody");
55 var state = 'pending';//初始按钮状态
56 var $btn=$("#btn");
57 var count=0;
58 var map=new HashMap();
59 //监听分块上传过程中的三个时间点
60 WebUploader.Uploader.register({
61 "before-send-file" : "beforeSendFile",
62 "before-send" : "beforeSend",
63 "after-send-file" : "afterSendFile",
64 }, {
65 //时间点1:所有分块进行上传之前调用此函数
66 beforeSendFile : function(file) {
67 var deferred = WebUploader.Deferred();
68 //1、计算文件的唯一标记,用于断点续传
69 // (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024)
70 (new WebUploader.Uploader()).md5File(file, 0, 1024)
71 .progress(function(percentage) {
72 $('#' + file.id).find("td.state").text("正在读取文件信息...");
73 }).then(function(val) {
74 fileMd5 = val;
75 $('#' + file.id).find("td.state").text("成功获取文件信息...");
76 //获取文件信息后进入下一步
77 deferred.resolve();
78 });
79 return deferred.promise();
80 },
81 //时间点2:如果有分块上传,则每个分块上传之前调用此函数
82 beforeSend : function(block) {
83 var deferred = WebUploader.Deferred();
84
85 $.ajax({
86 type : "POST",
87 url : "{% raw %}{{url_for('.checkChunk')}}{% endraw %}",
88 data : {
89 //文件唯一标记
90 fileMd5 : fileMd5,
91 //当前分块下标
92 chunk : block.chunk,
93 //当前分块大小
94 chunkSize : block.end - block.start
95 },
96 dataType : "json",
97 success : function(response) {
98 if (response.ifExist) {
99 //分块存在,跳过
100 deferred.reject();
101 } else {
102 //分块不存在或不完整,重新发送该分块内容
103 deferred.resolve();
104 }
105 }
106 });
107
108 this.owner.options.formData.fileMd5 = fileMd5;
109 deferred.resolve();
110 return deferred.promise();
111 },
112 //时间点3:所有分块上传成功后调用此函数
113 afterSendFile : function() {
114 //如果分块上传成功,则通知后台合并分块
115 $.ajax({
116 type : "POST",
117 url : "{% raw %}{{url_for('.mergeChunks')}}{% endraw %}",
118 data : {
119 fileMd5 : fileMd5,
120 fileSuffix:fileSuffix,
121 fileName:fileName,
122 },
123 success : function(response) {
124 console.log(response.fileName+" 上传成功")
125 // alert("上传成功");
126 }
127 });
128 }
129 });
130
131 var uploader = WebUploader
132 .create({
133 // swf文件路径
134 swf : 'https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.1/Uploader.swf',
135 // 文件接收服务端。
136 server : '{% raw %}{{ url_for("upload") }}{% endraw %}',
137 // 选择文件的按钮。可选。
138 // 内部根据当前运行是创建,可能是input元素,也可能是flash.
139 pick : {
140 id : '#picker',//这个id是你要点击上传文件的id
141 multiple : true
142 },
143 // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
144 resize : true,
145 auto : false,
146 //开启分片上传
147 chunked : true,
148 chunkSize : 10 * 1024 * 1024,
149
150 accept : {
151 extensions : "txt,jpg,jpeg,bmp,png,zip,rar,war,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx,iso,flv,mp4",
152 mimeTypes : '.txt,.jpg,.jpeg,.bmp,.png,.zip,.rar,.war,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.iso,.flv,.mp4'
153 }
154
155 });
156
157 // 当有文件被添加进队列的时候
158 uploader.on('fileQueued', function(file) {
159 //保存文件扩展名
160 fileSuffix=file.ext;
161 fileName=file.source['name'];
162 var fileSize=file.size;
163 var fileSizeStr="";
164 fileSizeStr=WebUploader.Base.formatSize(fileSize);
165 count++;
166 $list.append(
167 '<tr id="' + file.id + '" class="item" flag=0>'+
168 '<td class="index">' + count + '</td>'+
169 '<td class="info">' + file.name + '</td>'+
170 '<td class="size">' + fileSizeStr + '</td>'+
171 '<td class="state">等待上传...</td>'+
172 '<td class="percentage"></td>'+
173 '<td class="operate"><button name="upload" class="btn btn-warning">开始</button><button name="delete" class="btn btn-error">删除</button></td></tr>');
174 map.put(file.id+"",file);
175 });
176
177 // 文件上传过程中创建进度条实时显示。
178 uploader.on('uploadProgress', function(file, percentage) {
179 $('#' + file.id).find('td.percentage').text(
180 '上传中 ' + Math.round(percentage * 100) + '%');
181 });
182
183 uploader.on('uploadSuccess', function(file) {
184 $('#' + file.id).find('td.state').text('已上传');
185 });
186
187 uploader.on('uploadError', function(file) {
188 $('#' + file.id).find('td.state').text('上传出错');
189 });
190
191 uploader.on('uploadComplete', function(file) {
192 uploader.removeFile(file);
193 });
194
195
196 uploader.on('all', function(type) {
197 if (type === 'startUpload') {
198 state = 'uploading';
199 } else if (type === 'stopUpload') {
200 state = 'paused';
201 } else if (type === 'uploadFinished') {
202 state = 'done';
203 }
204
205 if (state === 'uploading') {
206 $btn.text('暂停上传');
207 } else {
208 $btn.text('开始上传');
209 }
210 });
211
212 $btn.on('click', function(){
213 if (state === 'uploading'){
214 uploader.stop(true);
215 } else {
216 uploader.upload();
217 }
218 });
219
220 $("body").on("click","#uploadTable button[name='upload']",function(){
221 flag=$(this).parents("tr.item").attr("flag")^1;
222 $(this).parents("tr.item").attr("flag",flag);
223 var id=$(this).parents("tr.item").attr("id");
224 if(flag==1){
225 $(this).text("暂停");
226 uploader.upload(uploader.getFile(id,true));
227
228 }else{
229 $(this).text("开始");
230 //uploader.stop(true);
231 uploader.stop(uploader.getFile(id,true));
232 //uploader.skipFile(file);
233 //uploader.removeFile(file);
234 //uploader.getFile(id,true);
235 }
236 });
237
238 $("body").on("click","#uploadTable button[name='delete']",function(){
239 var id=$(this).parents("tr.item").attr("id");
240 $(this).parents("tr.item").remove();
241 uploader.removeFile(uploader.getFile(id,true));
242 map.remove(id);
243 });
244 </script>
245 </body>
246 </html>
1 <!--
2 list.html
3 -->
4 <!DOCTYPE html>
5 <html lang="zh-CN">
6 <head>
7 <meta charset="utf-8">
8 <meta http-equiv="X-UA-Compatible" content="IE=edge">
9 <meta name="viewport" content="width=device-width, initial-scale=1">
10 <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
11 <title>Bootstrap 101 Template</title>
12
13 <!-- Bootstrap -->
14 <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
15
16 <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
17 <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
18 <!--[if lt IE 9]>
19 <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
20 <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
21 <![endif]-->
22 <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
23 <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
24 <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
25 <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
26 </head>
27 <body>
28 <div class="container-fluid">
29 <div class="table-responsive col-md-6 col-md-offset-3">
30 <table class="table">
31 <tr>
32 <td class="active">序号</td>
33 <td class="active">文件</td>
34 </tr>
35 {% raw %}{% for file in files %}
36 <tr>
37 <td class="active">{{ files.index(file)+1 }}</td>
38 <td class="active">{{file}}</td>
39 </tr>
40 {% endfor %}{% endraw %}
41 </table>
42 </div>
43 </div>
44 </body>
45 </html>
来源:https://www.cnblogs.com/a2534786642/p/11040887.html