plus_plugins icon indicating copy to clipboard operation
plus_plugins copied to clipboard

[Bug]: File name converted to UUID with "octet-stream" as extension when using a temporary file

Open Myzel394 opened this issue 2 years ago • 13 comments

Platform

Android 13

Plugin

share_plus

Version

6.3.1

Flutter SDK

3.9.0-0.2.pre

Steps to reproduce

  1. Create a temporary file
  2. Share it using shareXFiles

The example project works fine, but temporary files do not work. @Coronon this is related to #914

https://user-images.githubusercontent.com/50424412/227925332-afcd3320-f40e-4b34-bcfa-8bd686c84017.mp4

Code Sample

await Share.shareXFiles(
  [
    XFile.fromData(
      const Utf8Encoder().convert("test"),
      name: "viewkey.json",
    );
  ],
  text: "View key",
  subject: "Here's my View Key to see my location",
);


Here's the full Flutter code (ready to be copy-pasted):

```dart
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:share_plus/share_plus.dart';

void main() {
  runApp(const DemoApp());
}

class DemoApp extends StatefulWidget {
  const DemoApp({Key? key}) : super(key: key);

  @override
  DemoAppState createState() => DemoAppState();
}

class DemoAppState extends State<DemoApp> {
  String text = '';
  String subject = '';
  List<String> imageNames = [];
  List<String> imagePaths = [];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Share Plus Plugin Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0x9f4376f8),
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Share Plus Plugin Demo'),
          elevation: 4,
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(24),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              ElevatedButton(
                onPressed: () async {
                  await Share.shareXFiles([
                    XFile.fromData(
                    const Utf8Encoder().convert("test"),
                    name: "viewkey.json",
                  )],
                  text: "View key",
                  subject: "Here's my View Key to see my location",
                  );
                },
                child: const Text('Share'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}


### Logs

```shell
The only log generated when calling this function is this:


[+5894 ms] D/DecorView[](24650): onWindowFocusChanged hasWindowFocus false


### Flutter Doctor

```shell
[✓] Flutter (Channel beta, 3.9.0-0.2.pre, on Fedora Linux 37 (Workstation Edition) 6.2.7-200.fc37.x86_64, locale en_US.UTF-8)
    • Flutter version 3.9.0-0.2.pre on channel beta at /home/myzel394/snap/flutter/common/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 0df8557c56 (3 days ago), 2023-03-23 22:02:01 -0500
    • Engine revision 1cc4b2d280
    • Dart version 3.0.0 (build 3.0.0-290.3.beta)
    • DevTools version 2.22.2

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at /home/myzel394/Android/Sdk
    • Platform android-33, build-tools 33.0.2
    • Java binary at: /var/lib/snapd/snap/android-studio/125/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • clang version 10.0.0-4ubuntu1
    • cmake version 3.16.3
    • ninja version 1.10.0
    • pkg-config version 0.29.1

[✓] Android Studio (version 2021.3)
    • Android Studio at /var/lib/snapd/snap/android-studio/125/android-studio
    • Flutter plugin version 72.1.1
    • Dart plugin version 213.7433
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] IntelliJ IDEA Ultimate Edition (version 2022.3)
    • IntelliJ at /var/lib/snapd/snap/intellij-idea-ultimate/417
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] Connected device (3 available)
    • S426SXGG (mobile) • cda19d92 • android-arm64  • Android 12 (API 31)
    • Linux (desktop)    • linux    • linux-x64      • Fedora Linux 37 (Workstation Edition) 6.2.7-200.fc37.x86_64
    • Chrome (web)       • chrome   • web-javascript • Google Chrome 111.0.5563.110

[✓] Network resources
    • All expected network resources are available.

• No issues found!


### Checklist before submitting a bug

- [X] I Google'd a solution and I couldn't find it
- [X] I searched on StackOverflow for a solution and I couldn't find it
- [X] I read the README.md file of the plugin
- [X] I'm using the latest version of the plugin
- [X] All dependencies are up to date with `flutter pub upgrade`
- [X] I did a `flutter clean`
- [X] I tried running the example project

Myzel394 avatar Mar 27 '23 11:03 Myzel394

the same thing is happening to me, any fix?

amaan-honorfx avatar Mar 31 '23 08:03 amaan-honorfx

Hi there, while I was involved in large parts of the current share_plus code, the XFiles frontend was not designed by me. It would therefore probably be a little more time consuming for me to debug this. Sadly, I am currently unable to afford this time and can only leave you with the option to have a look at it after 27.04.2023 :/

Coronon avatar Mar 31 '23 13:03 Coronon

@Coronon Today is 31.03.2023 :O Do you know someone else who can look at this?

Myzel394 avatar Mar 31 '23 13:03 Myzel394

@Myzel394 pardon me, I meant 27.04.2023! Possibly 'miquelbeltran'? I don't want to ping him though as he is also often quite busy and will look at new issues when he has time on his hands.

A quick fix for the time being should be to actually write the data to disk as a named file in a cache directory, share them and then delete them afterwards/at app startup. Have you tried that yet? :)

Coronon avatar Mar 31 '23 15:03 Coronon

Yes temporarily I implemented that as a solution Using the path_provider package, get the temporary file directory and we can store the file there. Then we can share the file using share_plus.

code for storing the file temporarily

Future<String> storeFileTemporarily(Uint8List image) async {
  final tempDir = await getTemporaryDirectory();

  const uuid = Uuid();
  final path = '${tempDir.path}/${uuid.v4()}.jpg';
  final file = await File(path).create();
  file.writeAsBytesSync(image);

  return path;
}

then using the temporary file path share the file.

final path = await storeFileTemporarily(value);

await Share.shareXFiles(
         [XFile(path)],
);

Don't forget to import the packages.

amaan-honorfx avatar Apr 03 '23 09:04 amaan-honorfx

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days

github-actions[bot] avatar Jul 03 '23 00:07 github-actions[bot]

@Coronon could you take a look at this? :D

Myzel394 avatar Jul 03 '23 07:07 Myzel394

Actually have time today - will look into it ;)

Coronon avatar Jul 03 '23 09:07 Coronon

Ok, I took a look into this now. We are indeed just discarding the XFile.name value and just create a new filename with {uuidV4}.{extensionForMime}.

Technical:

After changing that to actually prefer the name attribute this still does not work which leads me to the underlying problem: The XFile implementation itself doesn't handle the name argument to the constructor in the io (non web) version - it simply discards it!

XFile.fromData(
   Uint8List bytes, {
   String? mimeType,
   String? path,
   String? name,
   int? length,
   DateTime? lastModified,
 })  : _mimeType = mimeType,
       _bytes = bytes,
       // This is where path will dictate the filename from now on <--------------
       _file = File(path ?? ''),
       _length = length,
       _lastModified = lastModified,
       // We simply discard `name` here <--------------
       super(path) {
   if (length == null) {
     _length = bytes.length;
   }
 }

The name is instead dynamically deduced from the provided path (yes, even when using fromData which also takes a path argument ^^):

// This is the dart:io implementation
// _file was initialized in the constructor from `path`
String get name => _file.path.split(Platform.pathSeparator).last;

Conclusion:

This sadly means that we can not implement this feature as easily as we would like to.

Either you continue with your workaround, and we add a little hint to this behavior in the documentation. Or we honor, by extension, the path argument in that we check if such a file exists and if not use the name getter to extract a filename the user want's us to use. This is pretty ugly though... The real problem lies with the upstream implementation of cross_file package in my eyes.

Another possible solution would be to provide a custom wrapper for XFile that implements the original interface and handles the name as expected, but that seems like a lot of scope creep.

Possible solution:

I could try to submit a PR to cross_file that defaults to the provided name if no path was provided (e.g. the file does not actually exist on disk). I don't know what the probability of them merging such a change is, though, as this might break other peoples code...

I would like to get the opinion of a maintainer here: /cc @vbuberen

Coronon avatar Jul 03 '23 12:07 Coronon

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days

github-actions[bot] avatar Oct 03 '23 00:10 github-actions[bot]

@Coronon Sorry for getting back that late. This issue slipped out of my radar and thanks to Stale Action something reminded me about it.

I have the same feeling that it would be good to open a PR into cross_file as you suggested.

vbuberen avatar Oct 05 '23 07:10 vbuberen

Just hit this as well. The name is completely ignored and my file becomes a UUID.

lovelessX avatar Nov 19 '23 19:11 lovelessX

Any update on this? Me, too, stumbled upon this. It would be at least nice to get this documented properly. At least a note in the XFile.fromData method comment would be nice.

marianhlavac avatar Dec 08 '23 21:12 marianhlavac

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days

github-actions[bot] avatar Mar 08 '24 00:03 github-actions[bot]