微信小程序 wx.uploadFile 的编码坑

本文介绍如何在微信小程序中实现图片和文本信息的同时上传,并解决上传过程中遇到的编码问题,确保中文等非ASCII字符正确传输。

From:http://blog.youkuaiyun.com/futurewu/article/details/53436222

转载一篇关于小程序往服务器上传图片和文本信息的博文吧,个人感觉很有帮助!因为之前我自己不知道在小程序中怎么把图片和文本信息同时上传到服务器,看了这篇博文后,才知道文本信息是需要添加到 formData:中进行传递。

编写微信小程序时,用到 wx.uploadFile,用来上传图片+文本信息.然而在编写过程中,由于官方的 demo 和文档描述很少,在几个坑上耗费了不少时间.

这里分享一个和编码有关的坑,主要是由于真机预览时 formData 中的非字母、数字的 ASCII 编码的字符如中文、泰文传输到服务端将不会自动进行转码码,并会会产生乱码及溢出,从而导致错误。

微信的 uploadFile 类似与 html 中的form带文件上传的表单 ( enctype=”multipart/form-data” ), 这样 post 上传的表单,可以包含文件,同时包含其它的键值数据。微信小程序用 uploadFile 实现类似的操作. 我的微信小程序端的uploadFile 代码如下 :

   wx.uploadFile({
            url: 'https://<upload_domain>/save',
            filePath:photoPath, //待上传的图片,由 chooseImage获得
            name:'food_image',
            formData: {
              latitude:0.0,
              longitude:0.0,
              restaurant_id:0,
              city:'北京',
              name:'beijing' // 名称
            }, // HTTP 请求中其他额外的 form data
            success: function(res){
              console.log("addfood success",res);
            },
            fail: function(res) {
              console.log("addfood fail",res);
            },
          })
},
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

服务端我用的 php 编写,这里接受文件的接口为 save ,我将  POST _FILES 里的数据直接输出到info.log 文件中用来进行调试.代码如下:

public function save(Request $request)
{
    error_log("FILES:" . json_encode($_FILES) . "\r\n", 3, "./logs/info.log");
    error_log("POST: " . json_encode($_POST) . "\r\n", 3, "./logs/info.log");
    error_log("city: " . $_POST["city"] . "\r\n", 3, "./logs/info.log");
    error_log("name: " . $_POST["name"] . "\r\n", 3, "./logs/info.log");
    error_log("latitude: " . $_POST["latitude"] . "\r\n", 3, "./logs/info.log");
    error_log("longitude: " . $_POST["longitude"] . "\r\n", 3, "./logs/info.log");
    error_log("restaurant_id: " . $_POST["restaurant_id"] . "\r\n", 3, "./logs/info.log");
    error_log("tags: " . $_POST["city"] . "\r\n", 3, "./logs/info.log");
  echo 'success';
} 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在小程序开发工具中运行小程序,选择好图片后进行上传操作,服务器端成功接收到数据,info.log输出的数据信息如下:

FILES:{"food_image":{"name":"store_265332457o6zAJszC4WsrwhUy55eh7iKJt7EQ1480318543139.jpg","type":"image\/jpeg","tmp_name":"\/tmp\/phpe3zGok","error":0,"size":845941}}
POST: {"latitude":"0","longitude":"0","restaurant_id":"0","tags":"","city":"\u5317\u4eac","name":"\u0e1b\u0e31\u0e01\u0e01\u0e34\u0e48\u0e07"}
city: 北京
name: ปักกิ่ง
latitude: 0
longitude: 0
restaurant_id: 0
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

( ps:php 中,FILES 里有 tmp_name 为收到了文件,将临时文件从该路径中移动到指定目录便可保存文件,这里看到有 /tmp/phpe3zGok 临时文件,说明成功收到了文件) 将小程序预览到手机上,点击上传,但却出现了问题,如下所示: 
这里写图片描述 
POST 数据为空, FILES 成功收到,而单独输出的$_POST数据出现乱码(中文和泰文部分),出现了溢出.

可以看到,非英文,数字的数据是乱码,而其它数据没有问题,显然是编码出现了问题,POST 数据输出为空,由于乱码导致了溢出使得格式错乱.

编码有问题,那么就尝试改变它的编码进行传输, uploadFile 的参数中加入

header{“chartset”:”utf-8”}
//或者 
header{"content-type":'application/x-www-form-urlencoded'}
   
  • 1
  • 2
  • 3

但是都没有什么效果,依然是在微信小程序工具中有效,而手机真机预览的时候出现乱码.在看到这篇帖子 【新手跳坑指南《三十九》wx.uploadFile】 才知道header 里的数据在真机预览的时候是无效的,需要改到 formData 中,受到了启发,尝试将编码数据加入formData 中,但仅仅传输了数据,并没有改变编码.依然是小程序开发工具中调试成功,而真机预览出了问题,这个估计要微信团队来回答了。 
知道 header 是个bug , 那么编码的操作暂时只能手动进行了,于是我将小程序段的数据都进行编码. 在 javascript 中,字符串编码函数是 encodeURI, 在小程序中尝试,也有该函数,所以我将小程序代码改为如下

wx.uploadFile({
            url: 'https://<upload_domain>/save',
            filePath:photoPath, //待上传的图片,由 chooseImage获得
            name:'food_image',
            formData: {
              latitude:encodeURI(0.0),
              longitude:encodeURI(0.0),
              restaurant_id:encodeURI(0),
              city:encodeURI('北京'),
              name:encodeURI('beijing') // 名称
            }, // HTTP 请求中其他额外的 form data
            success: function(res){
              console.log("addfood success",res);
            },
            fail: function(res) {
              console.log("addfood fail",res);
            },
          })
},
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

服务器端, php 进行 url 解码的函数是 urldecode

public function save(Request $request)
{
    error_log("FILES:" . json_encode($_FILES) . "\r\n", 3, "./logs/info.log");
    error_log("POST: " . json_encode($_POST) . "\r\n", 3, "./logs/info.log");
    error_log("city: " . urldecode($_POST["city"]) . "\r\n", 3, "./logs/info.log");
    error_log("name: " . urldecode($_POST["name"]) . "\r\n", 3, "./logs/info.log");
    error_log("latitude: " . urldecode($_POST["latitude"]) . "\r\n", 3, "./logs/info.log");
    error_log("longitude: " . urldecode($_POST["longitude"]) . "\r\n", 3, "./logs/info.log");
    error_log("restaurant_id: " . urldecode($_POST["restaurant_id"]) . "\r\n", 3, "./logs/info.log");
    error_log("tags: " . urldecode($_POST["city"]) . "\r\n", 3, "./logs/info.log");
    echo 'success';
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

再次测试,真机预览测试输出如下:

ILES:{"food_image":{"name":"jpeg","type":"image\/jpeg","tmp_name":"\/tmp\/php1svqDs","error":0,"size":9561}}
POST: {"restaurant_id":"0","tags":"","longitude":"0","latitude":"0","city":"%E5%8C%97%E4%BA%AC","name":"%E0%B8%9B%E0%B8%B1%E0%B8%81%E0%B8%81%E0%B8%B4%E0%B9%88%E0%B8%87"}
city: 北京
name: ปักกิ่ง
latitude: 0
longitude: 0
restaurant_id: 0
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到,所有数据都正常输出,其中POST 数据中 city 和 name未解码前是 urlencode 编码形式的数据,解码后正常输出. 
至此,我想使用小程序上传带文件信息到服务器端算是调通了, 希望这个方法能对出现同样问题的同学有一定帮助。

原文链接:微信小程序 wx.uploadFile 的编码坑 www.agent007.pw


<think>我们正在处理微信小程序通过wx.uploadFile上传的文件和表单数据。在微信小程序中,wx.uploadFile用于将本地资源上传到服务器。它会发起一个HTTPS POST请求,其中表单数据包括文件和其他字段。在服务器端(如使用Python的Flask框架),我们需要解析这种multipart/form-data格式的请求。 根据引用[1]和[2],我们注意到在小程序端,表单数据(包括文件)是通过wx.uploadFile上传的,并且在服务器端需要正确解析。特别地,引用[1]中提到了表单数据(form)可能包含多个字段,其中文件字段以数组形式存储,并且通过JSON.stringify进行序列化后上传。但是,请注意,实际上wx.uploadFile上传的文件和表单数据是以multipart/form-data格式传输的,而不是JSON格式。因此,在服务器端,我们需要使用能够解析multipart请求的库。 在Python中,我们可以使用Flask框架,它内置了对multipart/form-data请求的支持,我们可以通过request.files获取上传的文件,通过request.form获取表单中的其他字段。 然而,需要注意的是,在小程序端,如果表单数据中包含非文件字段,这些字段通常作为HTTP请求的part一起上传。在wx.uploadFile的API中,可以通过formData参数设置这些字段。 步骤: 1. 小程序端调用wx.uploadFile,指定url、filePath(本地文件路径)、name(文件对应的key)、formData(其他表单字段)等。 2. 服务器端(Python Flask)接收到请求,请求的Content-Type为multipart/form-data。 3. 使用request.files.get(name)获取文件对象,其中name为小程序端指定的文件key。 4. 使用request.form.get(field_name)获取其他表单字段。 但是,引用[2]提到了JSON.parse()和JSON.stringify(),这可能是因为在小程序端,有些开发者会将表单数据(非文件字段)作为一个JSON字符串上传,然后在服务器端解析。但这不是标准做法。标准做法是,将每个字段作为单独的part上传。 因此,我们有两种情况: 情况一:标准multipart/form-data上传,每个字段一个part(包括文件part和普通字段part)。 情况二:将普通字段作为一个JSON字符串上传(即只有一个字段,其值是JSON字符串),然后在服务器端解析该JSON字符串。 根据引用[1]中的代码,他们使用了`this.setData({ [str]: JSON.stringify(e.detail.list) })`,这可能是将文件列表序列化为JSON字符串存储到表单的一个字段中。但是,请注意,在wx.uploadFile中,文件字段是单独处理的,而其他表单字段通过formData设置。 因此,我们重点处理标准情况(情况一)。如果小程序端将多个字段作为JSON字符串上传,那么我们在服务器端需要解析这个JSON字符串。 下面,我们以Flask为例,展示如何解析这两种情况。 假设小程序端调用如下: ```javascript wx.uploadFile({ url: 'https://example.com/upload', filePath: tempFilePaths[0], name: 'file', formData: { 'user': 'test', 'info': JSON.stringify({ age: 25, gender: 'male' }) // 情况二:将一些复杂数据作为JSON字符串上传 }, success (res){ const data = res.data } }) ``` 在服务器端,我们这样处理: 情况一:直接获取普通字段(非JSON字符串的字段) 情况二:对于某些字段是JSON字符串的,我们需要额外解析。 示例代码(Flask): ```python from flask import Flask, request, jsonify import json app = Flask(__name__) @app.route('/upload', methods=['POST']) def upload_file(): # 获取文件 file = request.files.get('file') if file is None: return jsonify({'error': 'No file part'}), 400 # 保存文件或处理文件 # file.save('path/to/save') # 获取普通表单字段(非文件) user = request.form.get('user') # 直接获取,得到字符串'test' # 获取info字段,它是一个JSON字符串,需要解析 info_str = request.form.get('info') if info_str: try: info = json.loads(info_str) except json.JSONDecodeError: info = {} else: info = {} # 处理其他逻辑... return jsonify({'success': True, 'user': user, 'info': info}) ``` 但是,请注意,如果小程序端没有将info字段作为JSON字符串上传,而是直接作为多个字段(例如,formData: { age: 25, gender: 'male' }),那么我们在服务器端可以直接通过request.form.get('age')和request.form.get('gender')获取,而无需解析JSON。 因此,服务器端的处理取决于小程序端如何组织formData。 总结: 1. 对于文件,使用request.files获取。 2. 对于普通表单字段,使用request.form获取,每个字段都是字符串。 3. 如果某个字段的值是JSON字符串,则需要在服务器端对该字段进行JSON解析。 另外,需要注意的是,wx.uploadFile一次只能上传一个文件,但可以通过多次调用上传多个文件。如果一次上传多个文件,需要在小程序端将多个文件放在一个数组中,然后通过多个name(如file1, file2)来上传,但wx.uploadFile的API本身不支持一次传多个文件。因此,通常的做法是一次上传一个文件,或者使用其他方法(如将多个文件打包成zip上传)。 如果遇到需要同时上传多个文件的情况,可以考虑使用多个文件字段,例如: formData: { ... }, name: 'file1', // 这里只能指定一个name,所以一次只能传一个文件 // 因此,要传多个文件,需要多次调用,或者在一个请求中同时传多个文件(需要修改小程序端代码,使用多个name) 实际上,wx.uploadFile的name参数是当前上传文件的key,一次只能传一个文件。如果要在一个请求中上传多个文件,可以使用wx.request,并将多个文件作为数组放在formData中,但这样并不标准,因为formData不支持直接放文件对象。所以,通常的做法是使用多个wx.uploadFile,或者使用第三方库将文件转换为base64再通过wx.request上传(不推荐,因为base64会使数据变大)。 因此,我们回到问题:解析微信小程序通过wx.uploadFile上传的文件和表单数据。 在服务器端,我们按照multipart/form-data的格式解析即可。Flask的request对象已经帮我们处理好了。 注意:在Flask中,request.form是一个ImmutableMultiDict,可以获取所有普通表单字段。request.files是一个ImmutableMultiDict,可以获取所有上传的文件。 如果小程序端一次上传多个文件(通过多个字段名),例如: 文件1:name为'file1' 文件2:name为'file2' 那么服务器端可以通过request.files.get('file1')和request.files.get('file2')分别获取。 但是,wx.uploadFile一次只能传一个文件,所以多个文件需要多个请求,或者在一个请求中同时传多个文件(需要在小程序端使用多个wx.uploadFile是不可能的,因为每次调用只传一个文件)。因此,如果小程序端需要在一个请求中上传多个文件,应该使用wx.request,并且使用base64编码文件,或者使用其他方法。不过,wx.request不支持直接上传文件,所以通常还是用多个wx.uploadFile。 因此,我们最常见的场景是一个请求上传一个文件,同时附带一些表单字段。 最后,关于引用[1]中提到的:`this.setData({ [str]: JSON.stringify(e.detail.list) })`,这里e.detail.list可能是文件列表,但注意,在wx.uploadFile中,文件并不是通过formData传递的,而是通过filePath和name单独传递的。所以,这个JSON.stringify(e.detail.list)可能是用于存储文件信息(如路径、文件名等)到表单的某个字段,而不是上传文件内容。实际上传文件还是通过wx.uploadFile的filePath参数。 所以,在服务器端,我们可能还会收到一个包含文件信息的JSON字符串字段(比如文件列表的元数据),而实际的文件内容在另一个文件字段中。 因此,在解析时,我们除了获取文件内容,还需要解析这个文件信息字段(如果存在)。 示例:假设小程序端将文件列表的元数据作为formData的一个字段(如fileInfo)上传,其值是一个JSON字符串,包含多个文件的信息(即使只上传一个文件,也放在数组中)。同时,实际的文件内容通过文件字段(如file)上传。 服务器端代码: ```python file = request.files.get('file') # 获取文件对象 file_info_str = request.form.get('fileInfo') if file_info_str: file_info = json.loads(file_info_str) # file_info 应该是一个列表,包含每个文件的信息(如文件名、大小等) # 注意:这里只有一个文件内容(file对象),但file_info可能包含多个文件的信息?这取决于小程序端 # 如果小程序端一次只上传一个文件,那么file_info数组应该只有一个元素。 else: file_info = [] # 然后,我们可以将file_info中的信息与实际文件对象关联起来(例如,通过索引或文件名) # 但是,注意:如果一次上传多个文件,那么小程序端应该使用多个文件字段(如file0, file1, ...),并且file_info数组中的每个元素对应一个文件字段。 ``` 但是,由于wx.uploadFile一次只能上传一个文件,所以通常file_info数组中只有一个元素。 因此,我们按照以下步骤处理: 1. 获取上传的文件(通过request.files)。 2. 获取表单字段,特别是包含文件元数据的字段(如果有),并解析该字段的JSON字符串。 3. 将文件保存到服务器,并将元数据存储到数据库等。 注意:在保存文件时,我们可以使用file_info中的文件名,也可以使用上传的文件对象的文件名(file.filename)。 最后,关于安全性,我们需要对上传的文件进行验证(如文件类型、大小等),防止恶意文件上传。 以上即为使用Python解析微信小程序通过wx.uploadFile上传的文件和表单数据的方法。 下面,我们生成相关问题。</think>### 解析微信小程序上传的文件和表单数据(Python实现) 在微信小程序中,`wx.uploadFile` 用于上传文件,其请求结构为 **multipart/form-data** 格式,包含文件数据和表单字段。以下是 Python 解析方案: #### 1. 使用 Flask 框架解析 ```python from flask import Flask, request, jsonify import os app = Flask(__name__) UPLOAD_FOLDER = 'uploads' os.makedirs(UPLOAD_FOLDER, exist_ok=True) @app.route('/upload', methods=['POST']) def handle_upload(): # 获取上传的文件 uploaded_file = request.files.get('file') if not uploaded_file: return jsonify(error="No file uploaded"), 400 # 保存文件 filename = uploaded_file.filename filepath = os.path.join(UPLOAD_FOLDER, filename) uploaded_file.save(filepath) # 获取表单数据(JSON字符串) form_data_json = request.form.get('form') if form_data_json: try: # 解析JSON字符串 form_data = json.loads(form_data_json) image_list = form_data.get('image', []) video = form_data.get('video', '') # 处理解析后的数据 print(f"Received images: {image_list}") print(f"Received video: {video}") except json.JSONDecodeError: return jsonify(error="Invalid form data format"), 400 return jsonify( success=True, filename=filename, form_data=form_data if form_data_json else None ) if __name__ == '__main__': app.run(port=5000) ``` #### 2. 关键处理步骤 1. **文件获取** 通过 `request.files.get('file')` 获取上传的文件对象(字段名需与小程序的 `name` 参数一致) 2. **表单数据解析** - 表单数据存储在 `request.form` 中 - 使用 `json.loads()` 解析 JSON 格式的表单字符串[^1][^2] - 示例表单结构: ```json { "form": { "image": [ {"name": "Image1", "url": "https://...jpeg"}, {"name": "Image2", "url": "https://...png"} ], "video": "video_url_here" } } ``` #### 3. 小程序端配合示例 ```javascript // 微信小程序代码 wx.chooseImage({ success(res) { const tempFile = res.tempFiles[0]; wx.uploadFile({ url: 'http://your-python-server/upload', filePath: tempFile.path, name: 'file', // 与服务器request.files.get('file')对应 formData: { 'form': JSON.stringify({ // 序列化表单数据 image: [{name: tempFile.name, url: tempFile.path}], video: '' }) }, success(response) { console.log(JSON.parse(response.data)); } }); } }) ``` #### 4. 注意事项 - **字段一致性**:确保小程序 `formData` 字段名与服务器解析名一致 - **错误处理**:添加 JSON 解析异常捕获 - **安全验证**:实际部署需添加身份验证(如小程序 session_key 验证) - **文件存储**:生产环境应使用云存储(如 AWS S3、阿里云 OSS) > 此方案完整处理了文件保存和表单数据解析,符合微信小程序上传规范[^1][^2]。 --- ### 相关问题 1. 如何处理微信小程序同时上传多个文件? 2. 如何验证微信小程序上传请求的合法性? 3. 如何将上传的文件直接存储到云存储服务(如 AWS S3)? 4. 当表单数据包含嵌套 JSON 时如何优化解析过程? 5. 如何限制上传文件的大小和类型?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值