Python 读取 Android 文件权限被拒绝 [错误 13] '/storage/emulated/0" [kivy/buildozer]

问题描述 投票:0回答:1

我正在尝试将文件上传到应用程序的沙盒目录,以便我可以读取它并操作它并将其返回给用户 我既无法在 external_storage 中读取它,也无法将其复制到沙盒目录...我尝试的所有解决方案都说访问被拒绝。

现在我需要读取它并在沙盒目录中写入另一个文件,我可以在其中操作文件,或者直接上传或复制到那里。

def slctfl(self, selection):
    if check_permission(Permission.READ_EXTERNAL_STORAGE)== False:
        request_permission(Permission.READ_EXTERNAL_STORAGE)
    if check_permission(Permission.WRITE_EXTERNAL_STORAGE)== False:
        request_permission(Permission.WRITE_EXTERNAL_STORAGE)
    request_permissions([Permission.READ_EXTERNAL_STORAGE,Permission.WRITE_EXTERNAL_STORAGE])
    # I Logger Here <--> the check_permission (Read/write)  Both are True Which Means I have permissions
    if check_permission(Permission.READ_EXTERNAL_STORAGE)==True: # How On Earth ?
        afileinext=open(selection[0],'r') # The Line That Is Causing This Big Error :'(
        for e in afileinext:
            with open(main_directory+'/Assets/Avito/error_log.txt', 'a') as file:file.write(e+'\n')

SelectFile 是用 plyer 的文件选择器调用的,它返回文件的路径(我希望 plyer 是否真的可以读取该文件)

filechooser.open_file(on_selection=self.SelectFile)

Buildozer.spec

requirements = python3,kivy,kivymd,pyjnius,sqlite3,plyer
android.permissions = CALL_PHONE,WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE

我尝试用 pyjnius 执行此操作,它以 java 方式显示相同的访问被拒绝错误... 尝试使用 Shutils 将其复制到沙盒目录...同样的错误。 我尝试用意图来做到这一点..有点相同的错误,但这次说意图暴露在应用程序之外。
我尝试了 gpt 的冗长解决方案几乎让我精疲力竭...... gpt官方表示无法解决这个令人费解的问题,因为已经授予了权限,她和我尝试了各种解决方案 是否有明确的文档表明我不能使用除意图和活动之外需要权限的命令?

问题很简单我想将文件上传到沙盒目录...在外部存储上编辑它会是一个额外的好处,因为我稍后需要将文件处理回用户 我希望这就像在网页上上传文件一样简单...... 请帮助我,我在这个问题上浪费了太多时间,我不想尝试其他框架或其他编程语言,因为应用程序的用户界面已经是用 kivy 构建的。 数十亿个“为什么?”在我的脑海中浮现......为什么它会在“如果”中说访问被拒绝,以确保访问不被拒绝。

非常感谢!
快乐编码!调试愉快!

python android kivy file-permissions buildozer
1个回答
0
投票

[已解决]

这是一篇帮助我解决这个问题的文章:

https://groups.google.com/g/kivy-users/c/bjsG2j9bptI/m/-Oe_aGo0newJ

请注意,对此可能有更好的解决方案。

所以基本上我必须使用 Android Intents 来启动一个 Activity,该 Activity 允许用户选择文件并监听该 Activity 的结果以从文件中读取。

您可以使用

ACTION_PICK
ACTION_GET_CONTENT
ACTION_PICK
ACTION_GET_CONTENT
有点奇特,因为它打开一个本机文件选择器,允许用户在选择文件之前选择文件类型,对于每个文件类型,他都会被重定向到负责的本机电话应用程序用于读取这些文件并从那里选择文件(从谷歌照片或图库中选择图像,从文件浏览器中选择文件,从音乐应用程序中选择音乐...),而
ACTION_GET_CONTENT
从一个文件浏览器中选择所有文件类型...

import time
import os
from jnius import autoclass, cast
from android import activity
from android.permissions import Permission,request_permissions

#Getting The Path of tha main sandboxed direcorty of the app
#from where the app is running and is able to execute os commands
main_directory = os.path.dirname(os.path.abspath(__file__))

#Importing Java Classes
Intent = autoclass('android.content.Intent')
PythonActivity =  autoclass('org.kivy.android.PythonActivity')
File=autoclass('java.io.File')
FileOutputStream = autoclass('java.io.FileOutputStream')
InputStreamReader = autoclass('java.io.InputStreamReader')
BufferedInputStream = autoclass('java.io.BufferedInputStream')


#Requesting Read And Write Permissions To Read And Write in The External Storage 
#Wich is the Main storage of the owner of the device / SD Card 
#The Read Permission is sufficient for this case
request_permissions([Permission.READ_EXTERNAL_STORAGE,Permission.WRITE_EXTERNAL_STORAGE])

#To Identify The Result Of The Intent This Result Code Must Be Unique (At Each Search At One Session of the app [time where the app started activity till the activity end of the app ]))
#It Is Sent As a request Code(Identifer). When The intent sends back the result
#It Sends This Code With It We can then use it to identify the result of our search from all the results of the activities of the app
#if its not unique we can bypass the issue with choosing the last result.
#I Choosed To put the time variable since it is going to be unique at each second
#but if the if the time variable exceeds the max int size the app will crash
#the appropriate way to handle that is to store a variable at start of the app and keep incrementing it at each search
PICKFILE_RESULT_CODE=int(time.time())

#Getting The Current Activity of the app
currentActivity = cast('android.app.Activity', PythonActivity.mActivity)

#Defining the Function That Gets Triggered when the activity sends results
def on_activity_resultt(request_code, result_code, intent):            
    #Checking if the results are actually of the last activity request_code==PICKFILE_RESULT_CODE
    #And Cheking if the the user actually selected a file result_code==-1  [-1:::User Selected a file] [0:::User Canceled Selectin]
    if request_code==PICKFILE_RESULT_CODE and result_code==-1:
        uri = intent.getData() #Getting uri of the file
        #Could Anyone Explain The Following Lines? From What I Understand they seem to get a sort of content resolver from the app context
        #Then Read The Data From The uri 
        context = PythonActivity.mActivity.getApplicationContext() 
        content_resolver = context.getContentResolver() 
        input_stream = content_resolver.openInputStream(uri)
        input_reader = InputStreamReader(input_stream)
        buffered_input_stream = BufferedInputStream(input_stream)

        #Converting The Uri To A File Path And Getting the file name and extension 
        # To Allow Only Some Extensions this method is not enough you should add mimes to the ACTION_PICK or ACTION_GET_CONTENT
        # then use the file extension here to revalidate then validate through manually cheking the file with adequate libraries 
        # or by implementing your own methods        
        file_path = File(uri.getPath()).getPath()
        file_name=file_path.replace('\\','/').split('/')[-1]
        file_ext=file_name.split('.')[-1]

        #Choosing The Location In The Sandboxed Directory where you want to store the file
        output_stream = FileOutputStream(File(main_directory+'/Assets/'+file_name))
        #Creating The Array Buffer
        buffer = bytearray(1024)
        #Reading First 1024 bytes From the File Into The Buffer
        #Actually This Is Not Reading Byte By Byte As I Wanted It Is Doing It Charachter By Charachter
        #It Works Well On Csv xlsx and simple text files .... if you wanna do it with images or videos 
        #you should test it and maybe you should try to implement another reading method 
        #Also This is not tested with encoded texts you may need to add some few lines of code for that 
        num_read = buffered_input_stream.read(buffer)
        #A Loop That Keeps Reading From The input file into the buffer array and writing from the buffer into the output file
        while num_read > 0:
            output_stream.write(buffer, 0, num_read)
            num_read = buffered_input_stream.read(buffer)
        
        #Closing output/input streams 
        output_stream.close()
        buffered_input_stream.close()
        input_reader.close()
        input_stream.close()
#Defining new intent function 
#it does not get triggered actually but when i tried to bind the on_activity_result only it did not work somehow
def on_new_intentt(intent):
    pass

#binding the on result and the on new intent function to the ACTION_PICK Activity 
#And Starting The Activity
activity.bind(on_new_intent=on_new_intentt,on_activity_result=on_activity_resultt)
intent = Intent(Intent.ACTION_PICK)
intent.setType("*/*")
currentActivity.startActivityForResult(intent, PICKFILE_RESULT_CODE)

Buildozer.spec

requirements = python3,kivy,kivymd,pyjnius
android.permissions =WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,READ_INTERNAL_STORAGE

所以基本上我不知道为什么即使我有权限,Os.open 也没有读取文件的权限,但这是我的解释。 请告诉我你的看法?

Android 操作系统的设计方式是允许应用程序拥有一个沙盒目录,其中包含应用程序所需的所有资产,并在沙盒目录中安装应用程序背后的编程语言的解释器。该解释器仅安装在沙盒目录中,并且仅在该沙盒目录中具有几乎完全的权限。因此,即使应用程序具有必要的权限,我们也只能通过操作/意图/或活动来使用它们。从它的上下文来看,应用程序要求 Android 执行一项活动,Android 检查应用程序是否有权限,然后决定是否执行该活动

© www.soinside.com 2019 - 2024. All rights reserved.