GithubHelp home page GithubHelp logo

diachedelic / capacitor-blob-writer Goto Github PK

View Code? Open in Web Editor NEW
124.0 6.0 17.0 1.02 MB

Capacitor plugin to write binary data to the filesystem

License: MIT License

Ruby 4.70% Java 36.27% Objective-C 2.56% Swift 27.67% JavaScript 28.80%

capacitor-blob-writer's Introduction

capacitor-blob-writer

A faster, more stable alternative to @capacitor/filesystem's Filesystem.writeFile for writing Blobs to the filesystem.

Writing

import {Directory} from "@capacitor/filesystem";
import {Capacitor} from "@capacitor/core";
import write_blob from "capacitor-blob-writer";

// Firstly, get a reference to a Blob. This could be a file downloaded from the
// internet, or some binary data generated by your app.

let my_video_blob = ...;

// Secondly, write the Blob to disk. The 'write_blob' function takes an options
// object and returns a Promise, which resolves once the file has been
// successfully written.

write_blob({

// The 'path' option should be a string describing where to write the file. It
// may be specified as an absolute URL (beginning with "file://") or a relative
// path, in which case it is assumed to be relative to the 'directory' option.

    path: "media/videos/funny.mp4",

// The 'directory' option is used to resolve 'path' to a location on the disk.
// It is ignored if the 'path' option begins with "file://".

    directory: Directory.Data,

// The 'blob' option must be a Blob, which will be written to the file. The file
// on disk is overwritten, not appended to.

    blob: my_video_blob,

// Fast mode vastly improves read and write speeds on the web platform. For
// files written with 'fast_mode' set to true, Filesystem.readFile will produce
// a Blob rather than a Base64-encoded string. The 'fast_mode' option is
// ignored on iOS and Android. For backwards compatibility, it defaults to
// false.

    fast_mode: true,

// If the 'recursive' option is 'true', intermediate directories will be created
// as required. It defaults to 'false' if not specified.

    recursive: true,

// If 'write_blob' falls back to its alternative strategy on failure, the
// 'on_fallback' function will be called with the underlying error. This can be
// useful to diagnose slow writes. It is optional.

// See the "Fallback mode" section below for a detailed explanation.

    on_fallback(error) {
        console.error(error);
    }
}).then(function () {
    console.log("Video written.");
});

Reading

Reading a file is more complicated. Continuing the previous example, we stream a video file from disk using a <video> element.

const video_element = document.createElement("video");
document.body.append(video_element);

// The video file is accessed via a URL. How this URL is obtained depends on
// the platform.

if (Capacitor.getPlatform() === "web") {

// On the web platform, begin by reading the file.

    Filesystem.readFile({
        path: "media/videos/funny.mp4",
        directory: Directory.Data
    }).then(function ({data}) {

// For files written in Fast mode, the data is retrieved as a Blob. This is not
// true of files written using Filesystem.writeFile, where the data is
// retrieved as a string. A URL is created from the Blob.

        const url = URL.createObjectURL(data);
        video_element.src = url;

// To avoid memory leaks, the URL should be revoked when it is no longer
// needed.

        video_element.onended = function () {
            video_element.remove();
            URL.revokeObjectURL(url);
        };
    });
} else {

// It is much easier to get a URL on iOS and Android.

    Filesystem.getUri({
        path: "media/videos/funny.mp4",
        directory: Directory.Data
    }).then(function ({uri}) {
        video_element.src = Capacitor.convertFileSrc(uri);
    });
}

Installation

Different versions of the plugin support different versions of Capacitor:

Capacitor Plugin
v2 v0.2
v3 v1
v4 v1
v5 v1
v6 v1

Read the documentation for v0.2 here. See the changelog below for breaking changes.

npm install capacitor-blob-writer
npx cap update

iOS

Configure Info.plist to permit communication with the local BlobWriter server. This step is necessary for Capacitor v4+.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Run Product -> Clean Build Folder within Xcode if you experience weird runtime errors (#32).

Android

Configure AndroidManifest.xml to allow cleartext communication with the local BlobWriter server.

<application
    android:usesCleartextTraffic="true"
    ...

How it works

iOS/Android

When the plugin is loaded, an HTTP server is started on a random port, which streams authenticated PUT requests to disk, then moves them into place. The write_blob function makes the actual fetch call and handles the necessary authentication. Because browsers are highly optimised for network operations, this write does not block the UI.

I had dreamed of having the WebView intercept the PUT request and write the request's body to disk. Incredibly, neither iOS nor Android's webview are capable of correctly reading request bodies, due to this and this. Hence an actual webserver will be required for the forseeable future.

Web (Fast mode)

In contrast to Filesystem.writeFile, which stores the file contents as a Base64-encoded string, write_blob with fast_mode enabled stores the file as binary data. This is possible because IndexedDB, which is already used by the Filesystem plugin, supports the storage of Blob objects.

Fallback mode

On iOS and Android, there are times when write_blob inexplicably fails to communicate with the webserver, or the webserver fails to write the file. A fallback mode is provided, which invokes an alternative strategy if an error occurs. In fallback mode, the Blob is split into chunks and serially concatenated on disk using Filesystem.appendFile. While slower than Filesystem.writeFile, this strategy avoids Base64-encoding the entire Blob at once, making it stable for large Blobs.

There is no fallback mode on the web platform, because it is unnecessary.

Known limitations and issues

  • potential security risk (only as secure as GCDWebServer/nanohttpd), and also #12
  • no append option yet (see #11)

Benchmarks

The following benchmarks compare the performance and stability of Filesystem.writeFile with write_blob. See demo/src/index.ts for more details.

Android (Samsung A5)

Size Filesystem BlobWriter
1 kilobyte 18ms 89ms
1 megabyte 1009ms 87ms
8 megabytes 10.6s 0.4s
32 megabytes Out of memory[1] 1.1s
256 megabytes 17.5s
512 megabytes Quota exceeded[2]
  • [1] Crash java.lang.OutOfMemoryError
  • [2] File cannot be moved into the app's sandbox, I assume because the app's disk quota is exceeded

iOS (iPhone 6)

Size Filesystem BlobWriter
1 kilobyte 6ms 16ms
1 megabyte 439ms 26ms
8 megabytes 3.7s 0.2s
32 megabytes Out of memory[1] 0.7s
128 megabytes 3.1s
512 megabytes WebKit error[2]
  • [1] Crashes the WKWebView, which immediately reloads the page
  • [2] Failed to load resource: WebKit encountered an internal error

Google Chrome (Desktop FX-4350)

Size Filesystem BlobWriter (Fast mode)
1 kilobyte 4ms 9ms
1 megabyte 180ms 16ms
8 megabytes 1.5s 43ms
32 megabytes 5.2s 141ms
64 megabytes 10.5s 0.2s
512 megabytes Error[1] 1.1s
  • [1] DOMException: The serialized keys and/or value are too large

Changelog

v1.1.15

  • Adds support for Capacitor v6.

v1.1.11

  • Works around CapacitorHttp's patching of window.fetch.

v1.1.10

  • Adds support for Capacitor v5.

v1.1.1

  • Adds support for Capacitor v4.

v1.1.0

  • Introduces Fast mode, an opt-in feature which enables efficient Blob storage on the web platform.

v1.0.0

  • BREAKING: write_blob is now the default export of the capacitor-blob-writer package.
  • BREAKING: write_blob returns a string, not an object.
  • BREAKING: The data option has been renamed blob.
  • BREAKING: The fallback option has been removed. Now, fallback mode can not be turned off. However you can still detect when fallback mode has been triggered by supplying an on_fallback function in the options.
  • BREAKING: Support for Capacitor v2, and hence iOS v11, has been dropped.
  • Adds support for Capacitor v3.

capacitor-blob-writer's People

Contributors

azarattum avatar diachedelic avatar jamesdiacono avatar kristojorg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

capacitor-blob-writer's Issues

[Android] data parcel size error crashing application

We have use this plugin passing blob which is having data (1.3 Mb), though creating file successfully on my device but it crashing my application in between.
This the sample code:
const fileProcessData:blob;
await write_blob({
path: fileName,
directory: Directory.Data,
blob: fileProcessData,
fast_mode: true,
recursive: true,
}).then(success => {
console.log("Video written.");
}).catch(err => {
console.log("Video error");
});
The file is gettting created post crashing the application as per log.
tempsnip

Detail log:

2022-10-20 13:59:38.788 20203-20310 D/eglCodecCommon: setVertexArrayObject: set vao to 2 (2) 0 0
2022-10-20 13:59:42.554 20203-20333 V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 66914741, pluginId: BlobWriter, methodName: get_config
2022-10-20 13:59:42.557 20203-20333 V/Capacitor: callback: 66914741, pluginId: BlobWriter, methodName: get_config, methodData: {}
2022-10-20 13:59:42.574 20203-20333 V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 66914742, pluginId: Filesystem, methodName: getUri
2022-10-20 13:59:42.576 20203-20333 V/Capacitor: callback: 66914742, pluginId: Filesystem, methodName: getUri, methodData: {"path":"pdf_2022-10-20_13-59.pdf","directory":"DATA"}
2022-10-20 13:59:42.965 20203-20203 E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 1202648)
2022-10-20 13:59:42.969 20203-20203 D/AndroidRuntime: Shutting down VM
2022-10-20 13:59:43.003 20203-20203 E/AndroidRuntime: FATAL EXCEPTION: main
Process:, PID: 20203
java.lang.RuntimeException: Failure from system
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1675)
at android.app.Activity.startActivityForResult(Activity.java:4586)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:584)
at android.app.Activity.startActivityForResult(Activity.java:4544)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:570)
at android.app.Activity.startActivity(Activity.java:4905)
at android.app.Activity.startActivity(Activity.java:4873)
at com.getcapacitor.Bridge.launchIntent(Bridge.java:274)
at com.getcapacitor.BridgeWebViewClient.shouldOverrideUrlLoading(BridgeWebViewClient.java:27)
at tY.a(PG:3)
at xl.b(PG:81)
at org.chromium.android_webview.AwContentsClientBridge.shouldOverrideUrlLoading(PG:172)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.os.TransactionTooLargeException: data parcel size 1202648 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:1127)
at android.app.IActivityManager$Stub$Proxy.startActivity(IActivityManager.java:3754)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1669)
at android.app.Activity.startActivityForResult(Activity.java:4586) 
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:584) 
at android.app.Activity.startActivityForResult(Activity.java:4544) 
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:570) 
at android.app.Activity.startActivity(Activity.java:4905) 
at android.app.Activity.startActivity(Activity.java:4873) 
at com.getcapacitor.Bridge.launchIntent(Bridge.java:274) 
at com.getcapacitor.BridgeWebViewClient.shouldOverrideUrlLoading(BridgeWebViewClient.java:27) 
at tY.a(PG:3) 
at xl.b(PG:81) 
at org.chromium.android_webview.AwContentsClientBridge.shouldOverrideUrlLoading(PG:172) 
at android.os.MessageQueue.nativePollOnce(Native Method) 
at android.os.MessageQueue.next(MessageQueue.java:326) 
at android.os.Looper.loop(Looper.java:160) 
at android.app.ActivityThread.main(ActivityThread.java:6669) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
2022-10-20 13:59:43.017 20203-20380 D/Capacitor/BlobWriter: OPTIONS /data/user/0/files/pdf_2022-10-20_13-59.pdf
2022-10-20 13:59:43.216 20203-20203 I/Process: Sending signal. PID: 20203 SIG: 9
2022-10-20 13:59:43.216 20203-20380 D/Capacitor/BlobWriter: PUT /data/user/0/files/pdf_2022-10-20_13-59.pdf

v.1.1.8 Not support capacitor v3

[error] Analyzing dependencies
[!] CocoaPods could not find compatible versions for pod "CapacitorBlobWriter":
In Podfile:
CapacitorBlobWriter (from ../../node_modules/capacitor-blob-writer)

    Specs satisfying the `CapacitorBlobWriter (from `../../node_modules/capacitor-blob-writer`)` dependency were
    found, but they required a higher minimum deployment target.

Capacitor v3.4.1
Ionic v6

Question: Is the server externally accessible too?

The possibility to push files to the device from another, by letting them share credentials and port numbers would open up for some interesting use cases for multi device file sharing on a local network.

Error when building Android

Error when building Android:

First thing I need to say thanks for the great idea, to build in-app-server and write files. You did a good job on iOS but seemly Android does not work well. Please help.

  • I did the same as instructions step-by-step, built with ionic capacitor run android and finally click ▶️ in Android. Studio.
  • Expected: Running on my device
  • Result:
/....../node_modules/capacitor-blob-writer/android/src/main/java/com/equimaps/capacitorblobwriter/BlobWriter.java:58: error: no suitable method found for error(String,String,<null>)
            call.error("Server not running", "SERVER_DOWN", null);
                ^
    method PluginCall.error(String, Exception) is not applicable
      (actual and formal argument lists differ in length)
    method PluginCall.error(String) is not applicable

Build Output Detail Tab

Screen Shot 2020-07-29 at 15 48 22

Build Output

Screen Shot 2020-07-29 at 15 48 47

Error at line 58 of BlobWriter.java: server null but I don't know why

Screen Shot 2020-07-29 at 15 50 55

Thanks for your help!

Support for larger files

First, thanks for the plugin! I'm using this to download and then write a sqlite DB to memory. My initial DB size is 320MB and the plugin isn't able to write a file that size (at least on iOS on an iPhone 12 mini). The largest file I've been able to write so far is 270MB. The plugin was able to write the 320MB file on my laptop on the iOS simulator.

The plugin tries to use the fallback mode, but that also fails. It ends up creating the file but doesn't write any data into it.

The error I get is for the 320MB file is, "WebKit encountered an internal error."

Thanks for taking a look at this!

Screenshot 2023-05-04 at 9 05 11 AM

Question: Best Practice for Writing Large Files / Progress Indicator

@diachedelic: First just want to say thanks for the plugin. It's been a core feature in the app we are building. I do have a question for you and leaving it here in the hopes others may want the same info. In our app we are trying to support large files around 150 MBs and writing those to the device. We pull the file stream from our server and then grab the response data via response.blob(). When we go to write the file to device sometimes it succeeds and other times it fails. I also sometimes see the error mentioned in #9 but not sure that is related. My question is: Is there a recommended way to have more consistent file writes to device when the files exceed 100 MB in size.

Also, is there a way to get the progress of a file write? So that for large files we could display / track the progress to the users?

I'll provide a code sample below:

const writeFileToDevice = async (file, queryClient, callback) => {
  const isNative = Capacitor.isNativePlatform();
  const isOptimisticFile = validate(file.file_id);
  const fileIsAvailable = file?.status?.description === 'Available';

  // optimistic files writes are handled in useCreateVaultFiles when mutation fires
  if (isOptimisticFile || !isNative || !fileIsAvailable) return;

  return new Promise(async (resolve, reject) => {
    const { value } = await Storage.get({ key: file.storage_key });
    const devicePath = `${file.storage_key}-${file.name}`;

    if (!value) {
      const uninterceptedAxiosInstance = axios.create();
      const fileContent = await downloadFileContents({
        fileId: file.file_id,
        ...(file.workspace_id ? { workspaceId: file.workspace_id } : {}),
        queryClient,
      });

      const response = await uninterceptedAxiosInstance(fileContent, {
        method: 'GET',
        responseType: 'blob',
      });

      const blob = await response.data;
      try {
        const filePath = await write_blob({
          path: devicePath,
          blob: blob,
          directory: Directory.Data,
          recursive: true,
        });

        await Storage.set({
          key: file.storage_key,
          value: devicePath,
        });

        if (callback) {
          callback();
        }

        resolve({ storageKey: file.storage_key, filePath });
      } catch (e) {
        reject(e);
      }
    } else {
      resolve({ storageKey: file.storage_key, filePath: value });
    }
  });
};

export default writeFileToDevice;

[BUG][IOS] Can't write after coming back from background due to access control checks

Bug Report

On IOS and capacitor4, can't write after coming back from background due to access control checks. This is the error reported on console:

[Error] Could not connect to the server.
[Error] Fetch API cannot load http://localhost:53869/var/mobile/Containers/Data/Application/F7A3241E-E154-4C7C-A360-9971CD09A1E3/Documents/demo_0.10861703844695692.pdf due to access control checks.
[Error] Failed to load resource: Could not connect to the server. (demo_0.10861703844695692.pdf, line 0)

Versions

Current Behavior

After putting the app in the background and turning off the screen for a few seconds, it is not possible to write files again.

Follow the steps below to reproduce the issue:

  • Open app on IOS (real device)
  • Download some file and save it (write_blob(...))
  • Put app on background
  • Turn off the device screen for 5 seconds
  • Turn on screen a return to app
  • Try to download another file and save it (write_blob(...)).
  • Nothing is saved and can see the following errors on safari web console:
[Error] Could not connect to the server.
[Error] Fetch API cannot load http://localhost:53869/var/mobile/Containers/Data/Application/F7A3241E-E154-4C7C-A360-9971CD09A1E3/Documents/demo_0.10861703844695692.pdf due to access control checks.
[Error] Failed to load resource: Could not connect to the server. (demo_0.10861703844695692.pdf, line 0)

Code Reproduction

Sample app to reproduce de issue

https://github.com/dragermrb/app-sample-blob-writer

git clone [email protected]:dragermrb/app-sample-blob-writer.git
cd app-sample-blob-writer
npm install
npm run build
npx cap sync ios

Feature: Bigger chunk size

First of all its a greate plugin but I got the following question. Is their a possibility to increase the chunk size?

I got a Zip of around 500MB and need to unzip and save every file to the file system. This process takes quite some time with the blob_writer. Is there a way to increase the speed?

E/Capacitor/BlobWriter: failed to move file into place

any ideas as to why i would get this error here is a code snippet:
and yes the directory exists
const res = await fetch('https://cdn.vox-cdn.com/thumbor/Ous3VQj1sn4tvb3H13rIu8eGoZs=/0x0:2012x1341/1400x788/filters:focal(0x0:2012x1341):format(jpeg)/cdn.vox-cdn.com/uploads/chorus_image/image/47070706/google2.0.0.jpg');
const blob = await res.blob();

const { uri } = await writeFile({
  path: 'workbay',
  directory: FilesystemDirectory.Documents,

  // data must be a Blob (creating a Blob which wraps other data types
  // is trivial)
  data: blob,

  // create intermediate directories if they don't already exist
  // default: false
  recursive: true,

  // fallback to Filesystem.writeFile instead of throwing an error
  // (you may also specify a unary callback, which takes an Error and returns
  // a boolean)
  // default: true
});

}

Appending to a file

It would be good to be able to append to a file - then we could write even bigger files!

Add README

With sections:

  • What it does
  • Installation
  • Usage
  • How it works
  • References to Capacitor issues

Manipulate file with > 1GB

I saw this plugin is capable to read and write file with over > 256MB in size. However, both mobile platforms crashed while the size reaches 512MB.

I am developing an app which needs to unzip a zip file with >1GB, is it possible?

saving file in IOS photo gallery

Hi,

I would like to know if there's a way to see the downloaded media file in the IOS (Iphone) photo gallery and not only in the files folder

Thanks

definitions.d.ts missing from 1.0.0

Hi there 👋

Thanks for this great plugin!

I have been working with Capacitor 3 lately, and when installing 1.0.0, I don't see any definitions.d.ts in node_modules/capacitor-blob-writer.

Here's my ls of the node_modules/capacitor-blob-writer:

CapacitorBlobWriter.podspec blob_writer.js              ios
LICENSE                     blob_writer.umd.js          package.json
README.md                   blob_writer.umd.js.map
android

When I add https://github.com/diachedelic/capacitor-blob-writer/blob/master/definitions.d.ts directly to the folder, it seems to do the trick.

Filesystem.readFile after write returns invalid base64?

Thanks for this plugin, its working great on saving larger videos for me. I did find one (potential) issue - if I write a normal size file (e.g. photo from the camera) with this plugin and then read it back using Filsystem.readFile I seem to get malformed base64 result. Is this a known issue? At the moment I need to have separate logic for photos from the camera (saved with Filesystem.writeFile) and larger video files from the filesystem and it would be better to consolidate this.

I'm using capacitor v2 with v 0.2.4 of capacitor-blob-writer. My relevant imports look like this:

import { Plugins, Capacitor, CameraPhoto } from '@capacitor/core'
import { Camera, Filesystem } = Plugins
import { writeFile } from 'capacitor-blob-writer'

I just came across this ionic-team/capacitor-plugins#10 which seems to imply that Filesystem. support for binary data is not yet available so I guess if I wanted to get base 64 I'd need to use fetch or similar with a ConvertFileSrc url (and then convert the result).

thanks
Marcus

Errors in iOS log

Seems to occur as requests are received.

[ProcessSuspension]  0x10bafdeb0 - ProcessAssertion() PID 27900 Unable to acquire assertion for process with PID 27900
[ProcessSuspension] 0x10bafdeb0 - ProcessAssertion::processAssertionWasInvalidated()
[assertion] Error acquiring assertion: <NSError: 0x600001ba4300; domain: RBSAssertionErrorDomain; code: 2; reason: "Client is missing required entitlement"> {
    userInfo = {
        RBSAssertionAttribute = <RBSLegacyAttribute: 0x7f9f25539fe0; requestedReason: FinishTaskUnbounded; reason: FinishTaskUnbounded; flags: PreventTaskSuspend>;
    }
}

Documentation request: Improve option descriptions.

I'm converting an app from using cordova-plugin-file to using the Capacitor API. This plugin is a big help here (and should be a part of the official plugin IMHO), but I wonder if i am doing it right.

I am not sure I understand the point of the fallback: option.
What I think it does is the following:

  • The default is true, so by default all errors will cause fall back to trying Filesystem.writeFile() instead.
  • If fallback: is defined to be a function, that function is expected to take an error, and return a boolean, which i turn decides whether it should fall backs to Filesystem.writeFile() afterwards.
  • If fallback: is set to false or the set function returns false, the promise will reject with the error.
  • Filesystem.writeFile() fallback failing will result in the promise rejecting with an error.

Is this correct?

Also, how does the fallback to Filesystem.writeFile() work?

Bug Report: Error: unexpected HTTP status (only on Android)

I'm getting the error Error: unexpected HTTP status when trying to write any file on Android. I've used the instructions to install the plugin, but I'm not able to find what i'm doing wrong...

this is my code:

await writeFile({
          directory: FilesystemDirectory.External,
          path: `archives/${name}`,
          data: file,
          fallback: err => {
            console.error(err);
            return false;
          }
})

where ${name} is 3b4eaae1-a855-4161-9fd3-d0fe165dd27a.pdf and file is a Blob which was obtained via http request.

Also, the directory archives was created at the app initialization.

I'm using:
@capacitor/android 2.2.1
@capacitor/core 2.2.1
@capacitor/ios 2.2.1
ionic 5
angular 8
android emulator (Android 11, API 30)

In iOS works fine.

This is what I get in Chrome's console:


Error: unexpected HTTP status
at Module. (plugin.esm.js:73)
at Generator.next ()
at fulfilled (plugin.esm.js:21)
at ZoneDelegate.invoke (zone-evergreen.js:364)
at Zone.run (zone-evergreen.js:123)
at zone-evergreen.js:857
at ZoneDelegate.invokeTask (zone-evergreen.js:399)
at Zone.runTask (zone-evergreen.js:167)
at drainMicroTaskQueue (zone-evergreen.js:569)


And This is what I get in logcat:


2020-08-01 04:59:05.688 32318-32527/com.app V/Diagnostic: Get authorisation status for android.permission.READ_EXTERNAL_STORAGE
2020-08-01 04:59:05.731 32318-32527/com.app V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 29820049, pluginId: BlobWriter, methodName: getConfig
2020-08-01 04:59:05.731 32318-32527/com.app V/Capacitor: callback: 29820049, pluginId: BlobWriter, methodName: getConfig, methodData: {}
2020-08-01 04:59:05.755 32318-32527/com.app V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 29820050, pluginId: Filesystem, methodName: getUri
2020-08-01 04:59:05.756 32318-32527/com.app V/Capacitor: callback: 29820050, pluginId: Filesystem, methodName: getUri, methodData: {"path":"archives/3b4eaae1-a855-4161-9fd3-d0fe165dd27a.pdf","directory":"EXTERNAL"}
2020-08-01 04:59:05.867 32318-9786/com.app D/Capacitor/BlobWriter: OPTIONS /storage/emulated/0/Android/data/com.app/files/archives/3b4eaae1-a855-4161-9fd3-d0fe165dd27a.pdf
2020-08-01 04:59:06.965 32318-9786/com.app D/Capacitor/BlobWriter: PUT /storage/emulated/0/Android/data/com.app/files/archives/3b4eaae1-a855-4161-9fd3-d0fe165dd27a.pdf
2020-08-01 04:59:17.317 32318-9786/com.app E/Capacitor/BlobWriter: failed to move file into place
2020-08-01 04:59:17.335 32318-32318/com.app E/Capacitor/Console: File: http://192.168.1.74:8100/main.js - Line 12848 - Msg: Error: unexpected HTTP status


Benchmarks

Compare with Filesystem plugin. Maybe have a server which generates arbitrary length data, and write that to disk?

Web: Use blob storage for indexedDB

Using base64 for binary data results in 33% size overhead. This is unacceptable for my use case...

Also, if we use blobs, a valid blob URI can be returned from write_blob. The current README example does not work for the web platform right now, since the source url is invalid.

Bug Report: Run on Android but receiving "Not implement for web"

After hours, running & debugging on my devices, I found out it works well on iOS ✅ (iPad) but on Android ❌ (JOI 4G Lite) raises an error "Not implement for web" (debug on Chrome inspector).

For a clearer scenario, I'm running a feature with the following step:

Download encrypted file >>>> Read, decrypt and re-write a temp file

Screen Shot 2020-07-31 at 11 56 24

✅ Reading passed
✅ Decrypting passed
❌ Re-writing error

Here's my code:

Don't mind about path, I checked it.
Screen Shot 2020-07-31 at 12 10 50

Many thanks and please help!

Support Electron

If there is enough interest I will consider supporting Electron, and will definitely review pull requests. Please discuss here.

nativescript-capacitor and capacitor-blob-writer

Fails build android (nativescript-capacitor and capacitor-blob-writer)

Duplicate class fi.iki.elonen.NanoHTTPD found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$1 found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$AsyncRunner found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$ClientHandler found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$ContentType found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Cookie found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$CookieHandler found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$DefaultAsyncRunner found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$DefaultServerSocketFactory found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$DefaultTempFile found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$DefaultTempFileManager found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$DefaultTempFileManagerFactory found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$HTTPSession found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$IHTTPSession found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Method found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Response found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Response$1 found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Response$ChunkedOutputStream found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Response$IStatus found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$Response$Status found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$ResponseException found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$SecureServerSocketFactory found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$ServerRunnable found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$ServerSocketFactory found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$TempFile found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$TempFileManager found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)
Duplicate class fi.iki.elonen.NanoHTTPD$TempFileManagerFactory found in modules jetified-nanohttpd-2.3.1 (org.nanohttpd:nanohttpd:2.3.1) and jetified-nativescript-optimized-with-inspector-runtime (:nativescript-optimized-with-inspector:)

Go to the documentation to learn how to Fix dependency resolution errors.

Feature request: Lock plugin server port.

I just had a problem with using this plugin because of our apps Content-Security-Policy.
I had to add localhost:* to the policy to make the plugin work.

I do not like that I have to allow all ports to make this work, and thus my request is:
Add the possibility to lock the server to a specific port. With this possible, the Content-Security-Policy would not need to be so permissive.

Error when building on android

I'm getting an error when trying to build in android studio with this plugin enabled.

My java version is 20.0.1 and my gradle plugin version is 7.2.1 and gradle version is 7.4.2.

the error is quite long but here's the top part of it:

`Settings file 'C:\Users\Burrito\source\repos\spaces\spaces-angular\android\settings.gradle' line: 5

A problem occurred evaluating settings 'android'.

Could not open dsl generic class cache for script 'C:\Users\Burrito\source\repos\spaces\spaces-angular\android\capacitor.settings.gradle' (C:\Users\Burrito.gradle\caches\7.4.2\scripts\55li0ifujyrydye4p2funycmr).
BUG! exception in phase 'semantic analysis' in source unit 'BuildScript' Unsupported class file major version 64

  • Try:

Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

  • Exception is:
    org.gradle.api.GradleScriptException: A problem occurred evaluating settings 'android'.
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.lambda$apply$0(DefaultScriptPluginFactory.java:133)
    at org.gradle.configuration.DefaultScriptTarget.addConfiguration(DefaultScriptTarget.java:74)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:136)
    at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
    at org.gradle.configuration.BuildOperationScriptPlugin.lambda$apply$0(BuildOperationScriptPlugin.java:62)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
    at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:62)
    at org.gradle.initialization.ScriptEvaluatingSettingsProcessor.applySettingsScript(ScriptEvaluatingSettingsProcessor.java:73)
    at org.gradle.initialization.ScriptEvaluatingSettingsProcessor.process(ScriptEvaluatingSettingsProcessor.java:66)
    at org.gradle.initialization.SettingsEvaluatedCallbackFiringSettingsProcessor.process(SettingsEvaluatedCallbackFiringSettingsProcessor.java:34)
    at org.gradle.initialization.RootBuildCacheControllerSettingsProcessor.process(RootBuildCacheControllerSettingsProcessor.java:47)
    at org.gradle.initialization.BuildOperationSettingsProcessor$2.call(BuildOperationSettingsProcessor.java:50)
    at org.gradle.initialization.BuildOperationSettingsProcessor$2.call(BuildOperationSettingsProcessor.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.initialization.BuildOperationSettingsProcessor.process(BuildOperationSettingsProcessor.java:47)
    at org.gradle.initialization.DefaultSettingsLoader.findSettingsAndLoadIfAppropriate(DefaultSettingsLoader.java:136)
    at org.gradle.initialization.DefaultSettingsLoader.findAndLoadSettings(DefaultSettingsLoader.java:62)
    at org.gradle.initialization.SettingsAttachingSettingsLoader.findAndLoadSettings(SettingsAttachingSettingsLoader.java:34)
    at org.gradle.internal.composite.CommandLineIncludedBuildSettingsLoader.findAndLoadSettings(CommandLineIncludedBuildSettingsLoader.java:34)
    at org.gradle.internal.composite.ChildBuildRegisteringSettingsLoader.findAndLoadSettings(ChildBuildRegisteringSettingsLoader.java:48)
    at org.gradle.internal.composite.CompositeBuildSettingsLoader.findAndLoadSettings(CompositeBuildSettingsLoader.java:35)
    at org.gradle.initialization.InitScriptHandlingSettingsLoader.findAndLoadSettings(InitScriptHandlingSettingsLoader.java:34)
    at org.gradle.initialization.GradlePropertiesHandlingSettingsLoader.findAndLoadSettings(GradlePropertiesHandlingSettingsLoader.java:39)
    at org.gradle.initialization.DefaultSettingsPreparer.prepareSettings(DefaultSettingsPreparer.java:31)
    at org.gradle.initialization.BuildOperationFiringSettingsPreparer$LoadBuild.doLoadBuild(BuildOperationFiringSettingsPreparer.java:62)
    at org.gradle.initialization.BuildOperationFiringSettingsPreparer$LoadBuild.run(BuildOperationFiringSettingsPreparer.java:57)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
    at org.gradle.initialization.BuildOperationFiringSettingsPreparer.prepareSettings(BuildOperationFiringSettingsPreparer.java:45)
    at org.gradle.initialization.VintageBuildModelController.lambda$prepareSettings$2(VintageBuildModelController.java:85)
    at org.gradle.internal.model.StateTransitionController.lambda$doTransition$12(StateTransitionController.java:227)
    at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:238)
    at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:226)
    at org.gradle.internal.model.StateTransitionController.lambda$transitionIfNotPreviously$10(StateTransitionController.java:201)
    at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:34)
    at org.gradle.internal.model.StateTransitionController.transitionIfNotPreviously(StateTransitionController.java:197)
    at org.gradle.initialization.VintageBuildModelController.prepareSettings(VintageBuildModelController.java:85)
    at org.gradle.initialization.VintageBuildModelController.prepareToScheduleTasks(VintageBuildModelController.java:70)
    at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$prepareToScheduleTasks$2(DefaultBuildLifecycleController.java:134)
    at org.gradle.internal.model.StateTransitionController.lambda$doTransition$12(StateTransitionController.java:227)
    at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:238)
    at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:226)
    at org.gradle.internal.model.StateTransitionController.lambda$maybeTransition$9(StateTransitionController.java:187)
    at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:34)
    at org.gradle.internal.model.StateTransitionController.maybeTransition(StateTransitionController.java:183)
    at org.gradle.internal.build.DefaultBuildLifecycleController.prepareToScheduleTasks(DefaultBuildLifecycleController.java:132)
    at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.scheduleRequestedTasks(DefaultBuildTreeWorkPreparer.java:33)
    at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$doScheduleAndRunTasks$2(DefaultBuildTreeLifecycleController.java:89)
    at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph.withNewWorkGraph(DefaultIncludedBuildTaskGraph.java:75)
    at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.doScheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:88)
    at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$4(DefaultBuildTreeLifecycleController.java:106)
    at org.gradle.internal.model.StateTransitionController.lambda$transition$6(StateTransitionController.java:166)
    at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:238)
    at org.gradle.internal.model.StateTransitionController.lambda$transition$7(StateTransitionController.java:166)
    at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
    at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:166)
    at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:103)
    at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:69)
    at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:53)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.internal.buildtree.ProblemReportingBuildActionRunner.run(ProblemReportingBuildActionRunner.java:49)
    at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:69)
    at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:119)
    at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
    at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.lambda$execute$0(RootBuildLifecycleBuildActionExecutor.java:40)
    at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:128)
    at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.execute(RootBuildLifecycleBuildActionExecutor.java:40)
    at org.gradle.internal.buildtree.DefaultBuildTreeContext.execute(DefaultBuildTreeContext.java:40)
    at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.lambda$execute$0(BuildTreeLifecycleBuildActionExecutor.java:65)
    at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53)
    at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.execute(BuildTreeLifecycleBuildActionExecutor.java:65)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:61)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:57)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor.execute(RunAsBuildOperationBuildActionExecutor.java:57)
    at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.lambda$execute$0(RunAsWorkerThreadBuildActionExecutor.java:36)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:270)
    at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:119)
    at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.execute(RunAsWorkerThreadBuildActionExecutor.java:36)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecutor.execute(ContinuousBuildActionExecutor.java:103)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor.execute(SubscribableBuildActionExecutor.java:64)
    at org.gradle.internal.session.DefaultBuildSessionContext.execute(DefaultBuildSessionContext.java:46)
    at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:100)
    at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:88)
    at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:69)
    at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:62)
    at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:41)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:63)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:58)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:42)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
    at org.gradle.util.internal.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    Caused by: org.gradle.cache.CacheOpenException: Could not open dsl generic class cache for script 'C:\Users\Burrito\source\repos\spaces\spaces-angular\android\capacitor.settings.gradle' (C:\Users\Burrito.gradle\caches\7.4.2\scripts\55li0ifujyrydye4p2funycmr).
    at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:91)
    at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:43)
    at org.gradle.cache.internal.DefaultCacheFactory.doOpen(DefaultCacheFactory.java:103)
    at org.gradle.cache.internal.DefaultCacheFactory.open(DefaultCacheFactory.java:68)
    at org.gradle.cache.internal.DefaultCacheRepository$PersistentCacheBuilder.open(DefaultCacheRepository.java:117)
    at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler.compile(FileCacheBackedScriptClassCompiler.java:116)
    at org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache.getOrCompile(CrossBuildInMemoryCachingScriptClassCache.java:50)
    at org.gradle.groovy.scripts.internal.BuildScopeInMemoryCachingScriptClassCompiler.compile(BuildScopeInMemoryCachingScriptClassCompiler.java:50)
    at org.gradle.groovy.scripts.DefaultScriptCompilerFactory$ScriptCompilerImpl.compile(DefaultScriptCompilerFactory.java:49)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:125)
    at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
    at org.gradle.configuration.BuildOperationScriptPlugin.lambda$apply$0(BuildOperationScriptPlugin.java:62)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
    at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:62)
    at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.applyScript(DefaultObjectConfigurationAction.java:156)
    at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.access$000(DefaultObjectConfigurationAction.java:43)
    at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$1.run(DefaultObjectConfigurationAction.java:76)
    at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.execute(DefaultObjectConfigurationAction.java:190)
    at org.gradle.groovy.scripts.DefaultScript.apply(DefaultScript.java:128)
    at org.gradle.api.Script$apply.callCurrent(Unknown Source)
    at settings_bsumo2n5lkkj668aailmznckv.run(C:\Users\Burrito\source\repos\spaces\spaces-angular\android\settings.gradle:5)
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:91)
    ... 147 more
    Caused by: BUG! exception in phase 'semantic analysis' in source unit 'BuildScript' Unsupported class file major version 64
    at org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler.compileScript(DefaultScriptCompilationHandler.java:139)
    at org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler.compileToDir(DefaultScriptCompilationHandler.java:95)
    at org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler$2.run(BuildOperationBackedScriptCompilationHandler.java:54)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
    at org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler.compileToDir(BuildOperationBackedScriptCompilationHandler.java:51)
    at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$CompileToCrossBuildCacheAction.execute(FileCacheBackedScriptClassCompiler.java:190)
    at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$CompileToCrossBuildCacheAction.execute(FileCacheBackedScriptClassCompiler.java:170)
    at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$ProgressReportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:211)
    at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$ProgressReportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:194)
    at org.gradle.cache.internal.DefaultPersistentDirectoryCache$Initializer.initialize(DefaultPersistentDirectoryCache.java:100)
    at org.gradle.cache.internal.FixedSharedModeCrossProcessCacheAccess$1.run(FixedSharedModeCrossProcessCacheAccess.java:86)
    at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.doWriteAction(DefaultFileLockManager.java:216)
    at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.writeFile(DefaultFileLockManager.java:206)
    at org.gradle.cache.internal.FixedSharedModeCrossProcessCacheAccess.open(FixedSharedModeCrossProcessCacheAccess.java:83)
    at org.gradle.cache.internal.DefaultCacheAccess.open(DefaultCacheAccess.java:139)
    at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:89)
    ... 176 more
    Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 64
    at groovyjarjarasm.asm.ClassReader.(ClassReader.java:199)
    at groovyjarjarasm.asm.ClassReader.(ClassReader.java:180)
    at groovyjarjarasm.asm.ClassReader.(ClassReader.java:166)`

Question: Does the plugin handle larger files by "chunking" them on write and "stitching" them back together on read?

In looking at the Benchmarks each different device has a different "max" file size it can handle before it errors, my question pertains to how/if this plugin handles files with sizes that exceed what a specific device can handle.

Does it "chunk" them into smaller more manageable writes and "stitch" them back together on read?
Does it just throw an error if the file size is too large?

Audio quality on web and android

I am using ytdl-core to retrieve an mp3 file as a blob and it seems that the audio quality of the file is fine when downloaded directly from the server. However, when I store a blob using this plugin (web w/fast mode and android, haven't tested iOS) I experience a drop in the quality of the file. I am writing and reading the file as shown in the README but I can't seem to find the reason of this. Thanks in advance.

GDCWebServer not maintained, too old

Hey,
I'm a web dev with very scarce XCode experience. I tried installing your package but it fails to build now with this error in XCode:

      /Users/rasmus/Code/soundwheel/ios/App/Pods/Pods.xcodeproj: warning: The iOS deployment target
        'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 11.0 to
        16.4.99. (in target 'GCDWebServer' from project 'Pods')

Is there something I can do?

Wrong base64 encoding when retrieved back using Filesystem.readFile()

Hi.

First of all thank you for creating this great library. I have a requirement of storing big audio files on which Capacitor FileSystem.writeFile() failed on android devices (crashes when the file is too big) that this library seems to handle very well.

Now the issue I am facing is when I try to load it back:

const { data } = await Filesystem.readFile({
    path,
    directory: Directory.Documents
  });

const blob2 = new Blob([data], contentType);
const blob3 = (await fetch(`data:${contentType};base64,${data}`)).blob();

/**
 *  blob2 -> Failed to construct 'Blob': cannot convert to dictionary.
 *  blob3 ->TypeError: Failed to fetch
 */

Filesystem.readFile() suppose to load a base64 encoded data which I need to pass to an audio player, but as it failed to read it, blob2 and blob3 here are just my attempts to re-construct the blob with the loaded base64 to ensure it is correct.

What is strange to me, is while both failed on web and iOS environments, blob3 worked fine on android and re-converted base64 from it did also work in the player.

I have no idea if it is related to the way this library is storing the blob, if it is related to the way Filesystem.readFile() is reading it, or if it is something else I am doing wrong.

I made a live sandbox to reproduce it here.

Any help is appreciated. Thank you.

ExecException incorrectly extends interface error.

I tried using the blob storage plugin in my project. But when my build runs its throwing me the below error. I tried look at the issue why this plugin is providing issue with a type inside @types/nodes module of my project and found that you have been using error interface with code? as string which contradicts with what @types/nodes global config is.

I want to know what's the work around as my project dependencies are installed by npm on run time, same as well with capacitor blob storage plugin. I don't have control on both of the libraries.

image
image
image

BlobWriter does not respond to method call "get_config"

I updated to version 1.0 and configured it in my iOS app. When I try to write I file I see this error:

 TO JS {"exif":{"LensSpecification":[3.2999999999999998,3.2999999999999998,2.3999999999999999,2.3999999999999999],"ExposureMode":0,"FocalLenIn35mmFilm":31,"CompositeImage":2,"Orientation":1,"ExifVersion":"0232","ISOSpeedRatings":[640],"DateTimeOriginal":"2021:05:
⚡️  To Native ->  BlobWriter get_config 83184517
⚡️  Error: Plugin BlobWriter does not respond to method call "get_config" using selector "get_config:".
⚡️  Ensure plugin method exists, uses @objc in its declaration, and arguments match selector without callbacks in CAP_PLUGIN_METHOD.
⚡️  Learn more: https://capacitorjs.com/docs/plugins/ios/#defining-methods
⚡️  To Native ->  Filesystem getUri 83184518
⚡️  TO JS {"uri":"file:\/\/\/var\/mobile\/Containers\/Data\/Application\/582604F8-6B15-4311-A81B-B03DCB16E521\/Documents\/media\/attachments\/26fb1d50-46c1-4eaa-a929-073398d6ff3f.jpeg"}

Here is the section of code I am using to call write_blob():

return fetch(fileSrc)
            .then(result => result.blob())
            .then(data => {
                attachment.type = data.type.replace(/^([a-z]+)\//, '');
                const fileExtension = data.type.replace(/^([a-z]+)\//, '');
                const cleanFilename = sanitizeFilename(attachment.fileName);
                attachment.fileName = `${cleanFilename}.${fileExtension}`;
                return write_blob({
                    path: `media/attachments/${attachment.guid}.${attachment.type}`,
                    directory: Directory.Data,
                    data,
                    recursive: true,
                    fallback: err => {
                        console.error(
                            'falling back to filesystem.writefile',
                            err
                        );

                        return true;
                    }
                }).then(result => {

When I test the code from #30 writing the file works well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.