Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0]

### 21-01-2026

### Feature

- Allow reading parts of a file with `offset` and `length`.

## [1.0.3]

### 13-01-2026
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.outsystems.plugins.filesystem",
"version": "1.0.3",
"version": "1.1.0",
"private": true,
"dependencies": {},
"scripts": {
Expand Down
12 changes: 7 additions & 5 deletions packages/cordova-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,13 @@ Copy a file or directory

#### ReadFileOptions

| Prop | Type | Description | Since |
| --------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **`path`** | <code>string</code> | The path of the file to read | 1.0.0 |
| **`directory`** | <code><a href="#directory">Directory</a></code> | The <a href="#directory">`Directory`</a> to read the file from | 1.0.0 |
| **`encoding`** | <code><a href="#encoding">Encoding</a></code> | The encoding to read the file in, if not provided, data is read as binary and returned as base64 encoded. Pass <a href="#encoding">Encoding.UTF8</a> to read data as string | 1.0.0 |
| Prop | Type | Description | Default | Since |
| --------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | ----- |
| **`path`** | <code>string</code> | The path of the file to read | | 1.0.0 |
| **`directory`** | <code><a href="#directory">Directory</a></code> | The <a href="#directory">`Directory`</a> to read the file from | | 1.0.0 |
| **`encoding`** | <code><a href="#encoding">Encoding</a></code> | The encoding to read the file in, if not provided, data is read as binary and returned as base64 encoded. Pass <a href="#encoding">Encoding.UTF8</a> to read data as string | | 1.0.0 |
| **`offset`** | <code>number</code> | The offset to start reading the file from, in bytes. Native only (not available in web). Can be used in conjunction with length to partially read files. | <code>0</code> | 1.1.0 |
| **`length`** | <code>number</code> | The length of data to read, in bytes. Any non-positive value means to read to the end of the file. Native only (not available in web). Can be used in conjunction with offset to partially read files. | <code>-1</code> | 1.1.0 |


#### ReadFileInChunksOptions
Expand Down
2 changes: 2 additions & 0 deletions packages/cordova-plugin/android/OSFileErrors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,7 @@ fun Throwable.toFilesystemError(method: OSFileMethod): OSFileErrors.ErrorInfo =
is IONFILEExceptions.CopyRenameFailed.NoParentDirectory ->
OSFileErrors.missingParentDirectories

is IllegalArgumentException -> OSFileErrors.invalidInputMethod(method.methodName)

else -> OSFileErrors.operationFailed(method.methodName, this.localizedMessage ?: "")
}
27 changes: 25 additions & 2 deletions packages/cordova-plugin/android/OSFileOptions.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.outsystems.plugins.file

import io.ionic.libs.ionfilesystemlib.model.IONFILEConstants
import io.ionic.libs.ionfilesystemlib.model.IONFILEEncoding
import io.ionic.libs.ionfilesystemlib.model.IONFILEFolderType
import io.ionic.libs.ionfilesystemlib.model.IONFILEReadInChunksOptions
Expand All @@ -13,6 +14,8 @@ import org.json.JSONObject
private const val INPUT_PATH = "path"
private const val INPUT_DIRECTORY = "directory"
private const val INPUT_ENCODING = "encoding"
private const val INPUT_OFFSET = "offset"
private const val INPUT_LENGTH = "length"
private const val INPUT_CHUNK_SIZE = "chunkSize"
private const val INPUT_DATA = "data"
private const val INPUT_RECURSIVE = "recursive"
Expand Down Expand Up @@ -53,7 +56,15 @@ internal fun JSONObject.getReadFileOptions(): OSFileReadOptions? {
try {
val uri = getSingleIONFILEUri() ?: return null
val encoding = IONFILEEncoding.fromEncodingName(optString(INPUT_ENCODING))
return OSFileReadOptions(uri = uri, options = IONFILEReadOptions(encoding))
val offsetAndLength = getOffsetAndLength()
return OSFileReadOptions(
uri = uri,
options = IONFILEReadOptions(
encoding,
offset = offsetAndLength.first,
length = offsetAndLength.second
)
)
} catch (ex: JSONException) {
return null
}
Expand All @@ -67,9 +78,16 @@ internal fun JSONObject.getReadFileInChunksOptions(): OSFileReadInChunksOptions?
val uri = getSingleIONFILEUri() ?: return null
val encoding = IONFILEEncoding.fromEncodingName(optString(INPUT_ENCODING))
val chunkSize = getInt(INPUT_CHUNK_SIZE).takeIf { it > 0 } ?: return null
val offsetAndLength = getOffsetAndLength()
return OSFileReadInChunksOptions(
uri = uri,
options = IONFILEReadInChunksOptions(encoding = encoding, chunkSize = chunkSize)
options =
IONFILEReadInChunksOptions(
encoding = encoding,
chunkSize = chunkSize,
offset = offsetAndLength.first,
length = offsetAndLength.second
)
)
} catch (ex: JSONException) {
return null
Expand Down Expand Up @@ -146,6 +164,11 @@ internal fun JSONObject.getSingleIONFILEUri(): IONFILEUri.Unresolved? {
}
}

private fun JSONObject.getOffsetAndLength(): Pair<Int, Int> = Pair(
optInt(INPUT_OFFSET, 0).takeIf { it >= 0 } ?: 0,
optInt(INPUT_LENGTH, -1).takeIf { it > 0 } ?: IONFILEConstants.LENGTH_READ_TIL_EOF
)

private fun unresolvedUri(path: String, directoryAlias: String?) = IONFILEUri.Unresolved(
parentFolder = IONFILEFolderType.fromStringAlias(directoryAlias),
uriPath = path
Expand Down
2 changes: 1 addition & 1 deletion packages/cordova-plugin/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ allprojects {
}

dependencies{
implementation("io.ionic.libs:ionfilesystem-android:1.0.0@aar")
implementation("io.ionic.libs:ionfilesystem-android:1.1.0@aar")

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
Expand Down
Binary file not shown.
19 changes: 19 additions & 0 deletions packages/cordova-plugin/dist/definitions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,25 @@ export interface ReadFileOptions {
* @since 1.0.0
*/
encoding?: Encoding;
/**
* The offset to start reading the file from, in bytes.
* Native only (not available in web).
* Can be used in conjunction with length to partially read files.
*
* @since 1.1.0
* @default 0
*/
offset?: number;
/**
* The length of data to read, in bytes.
* Any non-positive value means to read to the end of the file.
* Native only (not available in web).
* Can be used in conjunction with offset to partially read files.
*
* @since 1.1.0
* @default -1
*/
length?: number;
}
export interface ReadFileInChunksOptions extends ReadFileOptions {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/cordova-plugin/ios/OSFileOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import IONFilesystemLib

enum OSFileOperation {
// Read Operations
case readFile(url: URL, encoding: IONFILEEncoding)
case readFileInChunks(url: URL, encoding: IONFILEEncoding, chunkSize: Int)
case readFile(url: URL, encoding: IONFILEEncoding, offset: Int, length: Int)
case readFileInChunks(url: URL, encoding: IONFILEEncoding, chunkSize: Int, offset: Int, length: Int)
case readdir(url: URL)
case stat(url: URL)
case getUri(url: URL)
Expand Down
16 changes: 8 additions & 8 deletions packages/cordova-plugin/ios/OSFileOperationExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ class OSFileOperationExecutor {
var resultData: PluginResultData?

switch operation {
case .readFile(let url, let encoding):
let fullData = try service.readEntireFile(atURL: url, withEncoding: encoding).textValue
case .readFile(let url, let encoding, let offset, let length):
let fullData = try service.readEntireFile(atURL: url, withEncoding: encoding, andOffset: offset, andLength: length).textValue
resultData = [OSFileConstants.ResultDataKey.data: fullData]
case .readFileInChunks(let url, let encoding, let chunkSize):
try processFileInChunks(at: url, withEncoding: encoding, chunkSize: chunkSize, for: operation, command)
case .readFileInChunks(let url, let encoding, let chunkSize, let offset, let length):
try processFileInChunks(at: url, withEncoding: encoding, chunkSize: chunkSize, offset: offset, length: length, for: operation, command)
return
case .write(let url, let encodingMapper, let recursive):
try service.saveFile(atURL: url, withEncodingAndData: encodingMapper, includeIntermediateDirectories: recursive)
Expand Down Expand Up @@ -61,9 +61,9 @@ class OSFileOperationExecutor {
}

private extension OSFileOperationExecutor {
func processFileInChunks(at url: URL, withEncoding encoding: IONFILEEncoding, chunkSize: Int, for operation: OSFileOperation, _ command: CDVInvokedUrlCommand) throws {
func processFileInChunks(at url: URL, withEncoding encoding: IONFILEEncoding, chunkSize: Int, offset: Int, length: Int, for operation: OSFileOperation, _ command: CDVInvokedUrlCommand) throws {
let chunkSizeToUse = chunkSizeToUse(basedOn: chunkSize, and: encoding)
try service.readFileInChunks(atURL: url, withEncoding: encoding, andChunkSize: chunkSizeToUse)
try service.readFileInChunks(atURL: url, withEncoding: encoding, andChunkSize: chunkSizeToUse, andOffset: offset, andLength: length)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
Expand All @@ -87,8 +87,8 @@ private extension OSFileOperationExecutor {
var path = ""
var method: OSFileMethod = OSFileMethod.getUri
switch operation {
case .readFile(let url, _): path = url.absoluteString; method = .readFile
case .readFileInChunks(let url, _, _): path = url.absoluteString; method = .readFileInChunks
case .readFile(let url, _, _, _): path = url.absoluteString; method = .readFile
case .readFileInChunks(let url, _, _, _, _): path = url.absoluteString; method = .readFileInChunks
case .write(let url, _, _): path = url.absoluteString; method = .writeFile
case .append(let url, _, _): path = url.absoluteString; method = .appendFile
case .delete(let url): path = url.absoluteString; method = .deleteFile
Expand Down
4 changes: 2 additions & 2 deletions packages/cordova-plugin/ios/OSFilePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private extension OSFilePlugin {
}

performSinglePathOperation(command, options) {
.readFile(url: $0, encoding: options.encoding)
.readFile(url: $0, encoding: options.encoding, offset: options.offset, length: options.length)
}
}

Expand All @@ -35,7 +35,7 @@ private extension OSFilePlugin {
}

performSinglePathOperation(command, options) {
.readFileInChunks(url: $0, encoding: options.encoding, chunkSize: options.chunkSize)
.readFileInChunks(url: $0, encoding: options.encoding, chunkSize: options.chunkSize, offset: options.offset, length: options.length)
}
}

Expand Down
14 changes: 14 additions & 0 deletions packages/cordova-plugin/ios/OSSinglePathFileOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,30 @@ class IONSinglePathRecursiveFileOptions: OSSinglePathFileOptions {

class IONReadFileOptions: OSSinglePathFileOptions {
let encoding: IONFILEEncoding
let offset: Int
let length: Int

enum CodingKeys: CodingKey {
case encoding
case offset
case length
}

required init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let encodingText = try container.decodeIfPresent(String.self, forKey: .encoding)
encoding = .create(from: encodingText)
if let decodedOffset = try container.decodeIfPresent(Int.self, forKey: .offset) {
offset = decodedOffset
} else {
offset = 0
}
if let decodedLength = try container.decodeIfPresent(Int.self, forKey: .length) {
length = decodedLength
} else {
length = -1
}

try super.init(from: decoder)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cordova-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "com.outsystems.plugins.filesystem",
"displayName": "Filesystem",
"version": "1.0.1",
"version": "1.1.0",
"description": "Filesystem plugin for Cordova",
"scripts": {
"lint": "npm run eslint && npm run prettier -- --check",
Expand Down
21 changes: 21 additions & 0 deletions packages/cordova-plugin/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,27 @@ export interface ReadFileOptions {
* @since 1.0.0
*/
encoding?: Encoding;

/**
* The offset to start reading the file from, in bytes.
* Native only (not available in web).
* Can be used in conjunction with length to partially read files.
*
* @since 1.1.0
* @default 0
*/
offset?: number;

/**
* The length of data to read, in bytes.
* Any non-positive value means to read to the end of the file.
* Native only (not available in web).
* Can be used in conjunction with offset to partially read files.
*
* @since 1.1.0
* @default -1
*/
length?: number;
}

export interface ReadFileInChunksOptions extends ReadFileOptions {
Expand Down
6 changes: 3 additions & 3 deletions plugin.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<plugin id="com.outsystems.plugins.filesystem" version="1.0.3" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
<plugin id="com.outsystems.plugins.filesystem" version="1.1.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
<name>OSFilePlugin</name>
<description>OutSystems' cordova geolocation plugin</description>
<author>OutSystems Inc</author>
Expand Down Expand Up @@ -60,8 +60,8 @@
<source url="https://cdn.cocoapods.org/"/>
</config>
<pods use-frameworks="true">
<pod name="IONFilesystemLib" spec="1.0.1" />
<pod name="IONFilesystemLib" spec="1.1.0" />
</pods>
</podspec>
</podspec>
</platform>
</plugin>