Skip to content

Commit 7449b31

Browse files
authored
File Support for web (#409)
* created files and started implementation * add different filetypes to sdk * Update README.md
1 parent fedd569 commit 7449b31

11 files changed

+228
-106
lines changed

README.md

+36-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ ParseCoreData().registerUserSubClass((username, password, emailAddress, {client,
146146
```
147147
Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`.
148148

149+
For custom file classes have a lock at [here](#File).
150+
149151
## Add new values to objects
150152
To add a variable to an object call and retrieve it, call
151153

@@ -809,11 +811,44 @@ QueryBuilder<ParseObject> query =
809811
..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId);
810812
```
811813

814+
## File
815+
There are three different file classes in this SDK:
816+
- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK.
817+
- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web.
818+
This class uses a `File` from `dart:io` for storing the raw file.
819+
- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web.
820+
This class uses an `Uint8List` for storing the raw file.
821+
822+
These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`.
823+
824+
Have a look at the example application for a small (non web) example.
825+
826+
827+
```dart
828+
//A short example
829+
Widget buildImage(ParseFileBase image){
830+
return FutureBuilder<ParseFileBase>(
831+
future: image.download(),
832+
builder: (BuildContext context,
833+
AsyncSnapshot<ParseFileBase> snapshot) {
834+
if (snapshot.hasData) {
835+
if (kIsWeb) {
836+
return Image.memory((snapshot.data as ParseWebFile).file);
837+
} else {
838+
return Image.file((snapshot.data as ParseFile).file);
839+
}
840+
} else {
841+
return CircularProgressIndicator();
842+
}
843+
},
844+
);
845+
}
846+
```
847+
812848
## Other Features of this library
813849
Main:
814850
* Installation (View the example application)
815851
* GeoPoints (View the example application)
816-
* Files (View the example application)
817852
* Persistent storage
818853
* Debug Mode - Logging API calls
819854
* Manage Session ID's tokens

lib/parse_server_sdk.dart

+7-40
Original file line numberDiff line numberDiff line change
@@ -22,81 +22,48 @@ import 'package:uuid/uuid.dart';
2222
import 'package:xxtea/xxtea.dart';
2323

2424
export 'src/network/parse_live_query.dart'
25-
if (dart.library.js) 'src/network/parse_live_query_web.dart';
25+
if (dart.library.js) 'src/network/parse_live_query_web.dart';
2626
export 'src/utils/parse_live_list.dart';
2727

28+
part 'package:parse_server_sdk/src/data/core_store.dart';
2829
part 'package:parse_server_sdk/src/data/parse_subclass_handler.dart';
2930
part 'package:parse_server_sdk/src/objects/response/parse_error_response.dart';
30-
3131
part 'package:parse_server_sdk/src/objects/response/parse_exception_response.dart';
32-
3332
part 'package:parse_server_sdk/src/objects/response/parse_response_builder.dart';
34-
3533
part 'package:parse_server_sdk/src/objects/response/parse_response_utils.dart';
36-
3734
part 'package:parse_server_sdk/src/objects/response/parse_success_no_results.dart';
38-
39-
part 'package:parse_server_sdk/src/data/core_store.dart';
40-
4135
part 'package:parse_server_sdk/src/storage/core_store_sem_impl.dart';
42-
4336
part 'package:parse_server_sdk/src/storage/core_store_sp_impl.dart';
44-
4537
part 'package:parse_server_sdk/src/storage/xxtea_codec.dart';
46-
4738
part 'src/base/parse_constants.dart';
48-
4939
part 'src/data/parse_core_data.dart';
50-
5140
part 'src/enums/parse_enum_api_rq.dart';
52-
5341
part 'src/network/parse_http_client.dart';
54-
5542
part 'src/network/parse_query.dart';
56-
5743
part 'src/objects/parse_acl.dart';
58-
5944
part 'src/objects/parse_base.dart';
60-
6145
part 'src/objects/parse_cloneable.dart';
62-
6346
part 'src/objects/parse_config.dart';
64-
6547
part 'src/objects/parse_error.dart';
66-
6748
part 'src/objects/parse_file.dart';
68-
49+
part 'src/objects/parse_file_base.dart';
50+
part 'src/objects/parse_file_web.dart';
6951
part 'src/objects/parse_function.dart';
70-
7152
part 'src/objects/parse_geo_point.dart';
72-
7353
part 'src/objects/parse_installation.dart';
74-
54+
part 'src/objects/parse_merge.dart';
7555
part 'src/objects/parse_object.dart';
76-
7756
part 'src/objects/parse_relation.dart';
78-
7957
part 'src/objects/parse_response.dart';
80-
8158
part 'src/objects/parse_session.dart';
82-
8359
part 'src/objects/parse_user.dart';
84-
8560
part 'src/utils/parse_date_format.dart';
86-
8761
part 'src/utils/parse_decoder.dart';
88-
8962
part 'src/utils/parse_encoder.dart';
90-
9163
part 'src/utils/parse_file_extensions.dart';
92-
9364
part 'src/utils/parse_logger.dart';
94-
95-
part 'src/utils/parse_utils.dart';
96-
9765
part 'src/utils/parse_login_helpers.dart';
98-
99-
part 'src/objects/parse_merge.dart';
66+
part 'src/utils/parse_utils.dart';
10067

10168
class Parse {
10269
ParseCoreData data;
@@ -163,7 +130,7 @@ class Parse {
163130
final ParseHTTPClient _client = client ??
164131
ParseHTTPClient(
165132
sendSessionId:
166-
sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId,
133+
sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId,
167134
securityContext: ParseCoreData().securityContext);
168135

169136
const String className = 'parseBase';

lib/src/base/parse_constants.dart

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const String keyClassInstallation = '_Installation';
3939
const String keyGeoPoint = 'GeoPoint';
4040
const String keyFile = 'File';
4141
const String keyRelation = 'Relation';
42+
const String keyFileClassname = 'ParseFile';
4243

4344
// Headers
4445
const String keyHeaderSessionToken = 'X-Parse-Session-Token';

lib/src/data/parse_core_data.dart

+24-12
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@ class ParseCoreData {
1414
///
1515
/// This class should not be user unless switching servers during the app,
1616
/// which is odd. Should only be user by Parse.init
17-
static Future<void> init(String appId, String serverUrl,
18-
{bool debug,
19-
String appName,
20-
String liveQueryUrl,
21-
String masterKey,
22-
String clientKey,
23-
String sessionId,
24-
bool autoSendSessionId,
25-
SecurityContext securityContext,
26-
CoreStore store,
27-
Map<String, ParseObjectConstructor> registeredSubClassMap,
28-
ParseUserConstructor parseUserConstructor}) async {
17+
static Future<void> init(
18+
String appId,
19+
String serverUrl, {
20+
bool debug,
21+
String appName,
22+
String liveQueryUrl,
23+
String masterKey,
24+
String clientKey,
25+
String sessionId,
26+
bool autoSendSessionId,
27+
SecurityContext securityContext,
28+
CoreStore store,
29+
Map<String, ParseObjectConstructor> registeredSubClassMap,
30+
ParseUserConstructor parseUserConstructor,
31+
ParseFileConstructor parseFileConstructor,
32+
}) async {
2933
_instance = ParseCoreData._init(appId, serverUrl);
3034

3135
_instance.storage ??=
@@ -59,6 +63,7 @@ class ParseCoreData {
5963
_instance._subClassHandler = ParseSubClassHandler(
6064
registeredSubClassMap: registeredSubClassMap,
6165
parseUserConstructor: parseUserConstructor,
66+
parseFileConstructor: parseFileConstructor,
6267
);
6368
}
6469

@@ -84,6 +89,10 @@ class ParseCoreData {
8489
_subClassHandler.registerUserSubClass(parseUserConstructor);
8590
}
8691

92+
void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
93+
_subClassHandler.registerFileSubClass(parseFileConstructor);
94+
}
95+
8796
ParseObject createObject(String classname) {
8897
return _subClassHandler.createObject(classname);
8998
}
@@ -95,6 +104,9 @@ class ParseCoreData {
95104
sessionToken: sessionToken, debug: debug, client: client);
96105
}
97106

107+
ParseFileBase createFile({String url, String name}) =>
108+
_subClassHandler.createFile(name: name, url: url);
109+
98110
/// Sets the current sessionId.
99111
///
100112
/// This is generated when a users logs in, or calls currentUser to update

lib/src/data/parse_subclass_handler.dart

+28-7
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,46 @@ typedef ParseObjectConstructor = ParseObject Function();
44
typedef ParseUserConstructor = ParseUser Function(
55
String username, String password, String emailAddress,
66
{String sessionToken, bool debug, ParseHTTPClient client});
7+
typedef ParseFileConstructor = ParseFileBase Function(
8+
{String name, String url});
79

810
class ParseSubClassHandler {
9-
10-
ParseSubClassHandler({Map<String, ParseObjectConstructor> registeredSubClassMap,
11-
ParseUserConstructor parseUserConstructor}){
12-
_subClassMap = registeredSubClassMap ?? Map<String, ParseObjectConstructor>();
11+
ParseSubClassHandler(
12+
{Map<String, ParseObjectConstructor> registeredSubClassMap,
13+
ParseUserConstructor parseUserConstructor,
14+
ParseFileConstructor parseFileConstructor}) {
15+
_subClassMap =
16+
registeredSubClassMap ?? Map<String, ParseObjectConstructor>();
1317
_parseUserConstructor = parseUserConstructor;
18+
if (parseFileConstructor != null)
19+
_parseFileConstructor = parseFileConstructor;
1420
}
1521

1622
Map<String, ParseObjectConstructor> _subClassMap;
1723
ParseUserConstructor _parseUserConstructor;
24+
ParseFileConstructor _parseFileConstructor = ({String name, String url}) {
25+
if (kIsWeb) {
26+
return ParseWebFile(null, name: name, url: url);
27+
} else {
28+
return ParseFile(null, name: name, url: url);
29+
}
30+
};
1831

1932
void registerSubClass(
2033
String className, ParseObjectConstructor objectConstructor) {
2134
if (className != keyClassUser &&
2235
className != keyClassInstallation &&
23-
className != keyClassSession)
24-
_subClassMap.putIfAbsent(className, () => objectConstructor);
36+
className != keyClassSession &&
37+
className != keyFileClassname)
38+
_subClassMap[className] = objectConstructor;
2539
}
2640

2741
void registerUserSubClass(ParseUserConstructor parseUserConstructor) {
28-
_parseUserConstructor ??= parseUserConstructor;
42+
_parseUserConstructor = parseUserConstructor;
43+
}
44+
45+
void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
46+
_parseFileConstructor = parseFileConstructor;
2947
}
3048

3149
ParseObject createObject(String classname) {
@@ -43,4 +61,7 @@ class ParseSubClassHandler {
4361
: ParseUser(username, password, emailAddress,
4462
sessionToken: sessionToken, debug: debug, client: client);
4563
}
64+
65+
ParseFileBase createFile({String name, String url}) =>
66+
_parseFileConstructor(name: name, url: url);
4667
}

lib/src/objects/parse_file.dart

+10-38
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
part of flutter_parse_sdk;
22

3-
class ParseFile extends ParseObject {
3+
class ParseFile extends ParseFileBase {
44
/// Creates a new file
55
///
66
/// {https://docs.parseplatform.org/rest/guide/#files/}
@@ -9,41 +9,17 @@ class ParseFile extends ParseObject {
99
String url,
1010
bool debug,
1111
ParseHTTPClient client,
12-
bool autoSendSessionId})
13-
: super('ParseFile', debug: debug, autoSendSessionId: autoSendSessionId) {
14-
_debug = isDebugEnabled(objectLevelDebug: debug);
15-
_client = client ??
16-
ParseHTTPClient(
17-
sendSessionId:
18-
autoSendSessionId ?? ParseCoreData().autoSendSessionId,
19-
securityContext: ParseCoreData().securityContext);
20-
21-
if (file != null) {
22-
name = path.basename(file.path);
23-
_path = '/files/$name';
24-
} else {
25-
name = name;
26-
url = url;
27-
}
28-
}
12+
bool autoSendSessionId})
13+
: super(
14+
name: file != null ? path.basename(file.path) : name,
15+
url: url,
16+
debug: debug,
17+
client: client,
18+
autoSendSessionId: autoSendSessionId,
19+
);
2920

3021
File file;
3122

32-
String get name => super.get<String>(keyVarName);
33-
set name(String name) => set<String>(keyVarName, name);
34-
35-
String get url => super.get<String>(keyVarURL);
36-
set url(String url) => set<String>(keyVarURL, url);
37-
38-
bool get saved => url != null;
39-
40-
@override
41-
Map<String, dynamic> toJson({bool full = false, bool forApiRQ = false}) =>
42-
<String, String>{'__type': keyFile, 'name': name, 'url': url};
43-
44-
@override
45-
String toString() => json.encode(toJson(full: true));
46-
4723
Future<ParseFile> loadStorage() async {
4824
final Directory tempPath = await getTemporaryDirectory();
4925

@@ -65,6 +41,7 @@ class ParseFile extends ParseObject {
6541
return this;
6642
}
6743

44+
@override
6845
Future<ParseFile> download() async {
6946
if (url == null) {
7047
return this;
@@ -81,11 +58,6 @@ class ParseFile extends ParseObject {
8158

8259
/// Uploads a file to Parse Server
8360
@override
84-
Future<ParseResponse> save() async {
85-
return upload();
86-
}
87-
88-
/// Uploads a file to Parse Server
8961
Future<ParseResponse> upload() async {
9062
if (saved) {
9163
//Creates a Fake Response to return the correct result

0 commit comments

Comments
 (0)