如何加载一个文件到Android上的流知道它的Jnet_Uri?

g6baxovj  于 2023-06-28  发布在  Android
关注(0)|答案(2)|浏览(128)

我正在用 Delphi 10.3 Rio编写一个Android FMX应用程序。在那里,我从图库中选择照片(通过TJIntent.JavaClass.ACTION_OPEN_DOCUMENT)并取回Jnet_Uri条目。我可以使用这些来读取图像EXIF(与TJExifInterface)。现在我还需要将这些图像加载到流中以进行进一步处理。我该怎么做?
当我尝试将Jnet_Uri转换为带有uri.getPath的路径时,结果类似于/document/image:26591uri.toString给了我content://com.android.providers.media.documents/document/image%3A26674TMemoryStream.LoadFromFile无法从以下两个路径加载:
无法打开文件“/document/image:26724”。没有这样的文件或目录
无法打开文件“/content:/com.android.providers.media.documents/document/image%3A26724”。不是目录
因此,问题是,如何知道一个Jnet_Uri来将文件内容加载到流中?

bnlyeluc

bnlyeluc1#

我可以通过JInputStream读取数据:

var
  uri: Jnet_Uri;
  ms: TMemoryStream;
  jis: JInputStream;
  b: TJavaArray<Byte>;
begin
  uri := .. some uri, alike "/document/image:26591"

  ms := TMemoryStream.Create;

  // Need to read via JInputStream, since Uri is not a file
  jis := TAndroidHelper.Context.getContentResolver.openInputStream(uri);
  b := TJavaArray<Byte>.Create(jis.available);
  jis.read(b);
  ms.Write(b.Data^, b.Length);
  jis.close;

   .. do something with Stream now
yyhrrdl8

yyhrrdl82#

我使用以下代码通过内容uri作为TStream访问Android上的文件:

stream := TipJavaContentResolverStream.Create(uri);

在 Delphi 11上测试,TipJavaContentResolverStream可以处理以ParcelFileDescriptor.AutoCloseInputStream打开的文件,我不知道TJIntent.JavaClass.ACTION_OPEN_DOCUMENT是否使用这个模型,试试看:

{
   The module contains a class for retrieving data from ContentResolver as TStream
   Author: Victor Fedorenkov
}
unit ipJavaContentResolverStreamUnit.Android;

interface

uses
  SysUtils, Types,

  Androidapi.JNI.Net,
  Androidapi.Jni.Os,
  Androidapi.Helpers,
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,

  ipJavaFileInputStreamUnit.Android;

type
  TipJavaContentResolverStream = class(TipJavaFileInputStream)
  private
    FContentUri: string;
  public
    constructor Create(const AContentUri: string);
  end;

implementation

const
  JParcelFileDescriptor_AutoCloseInputStream_ClassName = 'android.os.ParcelFileDescriptor$AutoCloseInputStream';

{ TipJavaContentResolverStream }

constructor TipJavaContentResolverStream.Create(const AContentUri: string);
var
  JUri: Jnet_Uri;
  JStream: JInputStream;
  ContentStreamClassName: string;
begin
  FContentUri := AContentUri;

  JUri := TJnet_Uri.JavaClass.parse(StringToJString(AContentUri));

  JStream := TAndroidHelper.Context.getContentResolver.openInputStream(JUri);

  if not Assigned(JStream) then
    raise Exception.Create('Unable get content stream: ' + AContentUri);

  ContentStreamClassName := JStringToString(JStream.getClass.getName);

  if ContentStreamClassName <> JParcelFileDescriptor_AutoCloseInputStream_ClassName then
    raise Exception.CreateFmt('Can''t open content class %s from %s', [
      ContentStreamClassName, AContentUri]);

  inherited Create(TJParcelFileDescriptor_AutoCloseInputStream.Wrap((JStream as ILocalObject).GetObjectID));
end;

end.
{
  Adapter for reading JFileInputStream as a regular TStream
  Author: Victor Fedorenkov
}

unit ipJavaFileInputStreamUnit.Android;

interface

uses
  SysUtils, Classes, Math, RTLConsts,

  Androidapi.Jni,
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
  TipJavaFileInputStream = class(TStream)
  private
    FJStream: JFileInputStream;
  protected
      function GetSize: Int64; override;
  public
    constructor Create(AJStream: JFileInputStream);

    function Read(var Buffer; Count: LongInt): LongInt; override;
    function Write(const Buffer; Count: LongInt): LongInt; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;

    property JStream: JFileInputStream read FJStream;
  end;

implementation

{ TipJavaFileInputStream }

constructor TipJavaFileInputStream.Create(AJStream: JFileInputStream);
begin
  FJStream := AJStream;
end;

function TipJavaFileInputStream.GetSize: Int64;
begin
  Result := FJStream.getChannel.size;
end;

function TipJavaFileInputStream.Write(const Buffer; Count: LongInt): LongInt;
begin
  raise EStreamError.CreateRes(@SWriteError);
end;

function TipJavaFileInputStream.Read(var Buffer; Count: LongInt): LongInt;
var
  CanRead: LongInt;
  ReadRes: Integer;
  b: TJavaArray<Byte>;
begin
  Result := 0;

  b := nil;
  try
    repeat
      // Calculate how much we can read from the current position
      CanRead := Min(FJStream.available, Count - Result);

      // Allocate a buffer for reading if it doesn't exist or is not large enough
      if (not Assigned(b)) or (b.Length < CanRead) then
      begin
        FreeAndNil(b);
        b := TJavaArray<Byte>.Create(CanRead);
      end;

      // Read the data
      ReadRes := FJStream.read(b, 0, CanRead);

      // Copy what was successfully read into the buffer
      if ReadRes > 0 then
      begin
        Move(b.Data^, (PByte(@Buffer) + Result)^, ReadRes);
        Inc(Result, ReadRes);
      end;

      // Continue reading until there is nothing left to read
    until ReadRes <= 0;

  finally
    FreeAndNil(b);
  end;
end;

function TipJavaFileInputStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var
  NewPosition: Int64;
begin
  // To avoid a warning
  NewPosition := 0;

  case Origin of
    soBeginning: NewPosition := Offset;
    soCurrent: NewPosition := FJStream.getChannel.position + Offset;
    soEnd: NewPosition := Size + Offset;
  end;

  Result := FJStream.getChannel.position(NewPosition).position;
end;

end.

相关问题