Android平台使用Python(Kivy框架)保存文件时遭遇Errno 13权限拒绝问题
解决Kivy Android应用下载MP3到Download文件夹时的Errno 13权限拒绝问题
这个权限拒绝的坑在Kivy开发Android应用时太常见了,本质是Android的权限管控机制在起作用——哪怕你要写入的是公开的Download目录,也不能直接跳过权限申请就操作,尤其是Android 6.0(API 23)及以上的系统,必须动态申请权限才行。下面给你一步步的解决方案:
1. 先配置必要的权限与依赖
Android要求应用必须明确申请WRITE_EXTERNAL_STORAGE权限才能写入外部存储,同时下载需要INTERNET权限。如果用buildozer打包,先在buildozer.spec里做以下配置:
- 添加plyer依赖(用来处理动态权限申请):
requirements = python3,kivy,plyer
- 声明所需权限:
android.permissions = WRITE_EXTERNAL_STORAGE, INTERNET
2. 修改代码:先申请权限再执行下载
把下载逻辑和权限申请绑定,确保拿到权限后再操作文件:
import requests from plyer import permissions from kivy.app import App from kivy.uix.button import Button class MP3Downloader(App): def build(self): download_btn = Button(text="Download MP3") download_btn.bind(on_press=self.start_download) return download_btn def start_download(self, instance): # 先检查是否已有权限 if permissions.check_permission('WRITE_EXTERNAL_STORAGE'): self.download_and_save() else: # 动态申请权限,回调处理结果 permissions.request_permissions( ['WRITE_EXTERNAL_STORAGE'], self.handle_permission_result ) def handle_permission_result(self, permissions_list, results): # 所有权限通过才继续执行 if all(results): self.download_and_save() else: print("权限被拒绝,无法完成下载") def download_and_save(self): target_link = 'https://www.yt-download.org/download/IFtwhMK64H8/mp3/128/1630338605/f2803874069bf196561631cea2b1b11c2b1d2f9555e2baf751eb28b46d484bb5/0.mp3' try: response = requests.get(target_link) # 写入Download文件夹 with open('/storage/emulated/0/Download/file.mp3', 'wb') as f: f.write(response.content) print("下载完成!") except Exception as e: print(f"下载过程出错: {str(e)}") if __name__ == '__main__': MP3Downloader().run()
3. Android 10+的适配方案(Scoped Storage)
如果你的应用目标SDK是29及以上,Android的Scoped Storage机制会限制直接写入公共目录,这时候推荐用MediaStore来插入文件,更符合系统规范且无需额外权限:
from android.media import MediaStore from android.os import Environment from kivy.app import App import requests def save_to_download(content, filename): # 获取Download目录的MediaStore URI download_uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI # 配置文件元数据 file_details = { MediaStore.Downloads.DISPLAY_NAME: filename, MediaStore.Downloads.MIME_TYPE: 'audio/mpeg', MediaStore.Downloads.RELATIVE_PATH: Environment.DIRECTORY_DOWNLOADS } # 获取ContentResolver操作MediaStore resolver = App.get_running_app().content_resolver # 插入文件记录到系统媒体库 file_uri = resolver.insert(download_uri, file_details) # 写入文件内容 with resolver.openOutputStream(file_uri, 'w') as output_stream: output_stream.write(content) return file_uri # 在download_and_save里替换原open写法: response = requests.get(target_link) save_to_download(response.content, 'file.mp3')
内容的提问来源于stack exchange,提问作者ruslol228




