archive
archive copied to clipboard
ArchiveException: Could not find End of Central Directory Record
E/flutter (18353): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter (18353): ArchiveException: Could not find End of Central Directory Record
E/flutter (18353): #0 ZipDirectory._findSignature (package:archive/src/zip/zip_directory.dart:145:5)
E/flutter (18353): #1 new ZipDirectory.read (package:archive/src/zip/zip_directory.dart:27:20)
E/flutter (18353): #2 ZipDecoder.decodeBuffer (package:archive/src/zip_decoder.dart:21:21)
E/flutter (18353): #3 ZipDecoder.decodeBytes (package:archive/src/zip_decoder.dart:17:12)
E/flutter (18353): #4 writeCounter (file:///C:/src/projetos/widget_dinamico/lib/main.dart:83:38)
E/flutter (18353):
I already do many checks of file type and/or content. I already criate a new ZIP file in computer, transfer to phone and test, and got same error.
My code is:
import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:flutter_document_picker/flutter_document_picker.dart';
import 'package:archive/archive.dart';
import 'package:archive/archive_io.dart';
const kAndroidUserAgent =
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/teste3.xhtml');
}
final data = loadAsset();
String selectedUrl;
String extPath;
//recuperando localpath
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
print(directory.path);
extPath = directory.path;
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/teste.zip');
}
Future<File> writeCounter(String counter) async {
final path = await FlutterDocumentPicker.openDocument();
final ffile = await _localFile;
File(path).copy(ffile.path);
// Read the Zip file from disk.
List<int> bytes = new File(ffile.path).readAsBytesSync();
// Decode the Zip file
Archive archive = new ZipDecoder().decodeBytes(bytes);
// Extract the contents of the Zip archive to disk.
for (ArchiveFile cfile in archive) {
String filename = cfile.name;
if (cfile.isFile) {
List<int> cdata = cfile.content;
new File('out/' + filename)
..createSync(recursive: true)
..writeAsBytesSync(cdata);
} else {
new Directory('out/' + filename)
..create(recursive: true);
}
}
selectedUrl = extPath + '/out/OEBPS/1102014248.xhtml';
// Write the file
return File(extPath + '/out/OEBPS/1102014248.xhtml'); // file.writeAsString('$counter');
}
void main() async {
// escrevendo no arquivo
writeCounter(await data);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
final flutterWebviewPlugin = new FlutterWebviewPlugin();
final _codeCtrl =
new TextEditingController(text: '' );
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter WebView Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_) => new
WebviewScaffold(
url: selectedUrl,
appBar: new AppBar(
title: const Text('Widget webview'),
actions: <Widget>[
new RaisedButton(
onPressed: () {
// escrevendo no arquivo
//data.then((String rres){writeCounter(rres);});
/* abrindo o epub */
//selectedUrl = "file:///data/user/0/com.example.widgetdinamico/app_flutter/teste.xhtml";
final future =
flutterWebviewPlugin.evalJavascript(_codeCtrl.text);
future.then( (String result) {
//_history.add('eval: $result');
print('eval: $result');
});
},
child: const Text('Color')
)
],
),
clearCache: true,
clearCookies: true,
withZoom: false,
withLocalStorage: true,
)
},
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Instance of WebView plugin
final flutterWebviewPlugin = new FlutterWebviewPlugin();
// On destroy stream
StreamSubscription _onDestroy;
// On urlChanged stream
StreamSubscription<String> _onUrlChanged;
// On urlChanged stream
StreamSubscription<WebViewStateChanged> _onStateChanged;
StreamSubscription<WebViewHttpError> _onHttpError;
StreamSubscription<double> _onScrollYChanged;
StreamSubscription<double> _onScrollXChanged;
final _urlCtrl = new TextEditingController(text: selectedUrl);
final _codeCtrl =
new TextEditingController(text: "confirm('teste');" /*'window.navigator.userAgent'*/); /* \$('rb-f').css({"color": "red", "border": "2px solid red"}); */
final _scaffoldKey = new GlobalKey<ScaffoldState>();
final _history = [];
@override
void initState() {
super.initState();
flutterWebviewPlugin.close();
_urlCtrl.addListener(() {
selectedUrl = _urlCtrl.text;
});
// Add a listener to on destroy WebView, so you can make came actions.
_onDestroy = flutterWebviewPlugin.onDestroy.listen((_) {
if (mounted) {
// Actions like show a info toast.
_scaffoldKey.currentState.showSnackBar(
const SnackBar(content: const Text('Webview Destroyed')));
}
});
// Add a listener to on url changed
_onUrlChanged = flutterWebviewPlugin.onUrlChanged.listen((String url) {
print('_onUrlChanged');
if (mounted) {
setState(() {
_history.add('onUrlChanged: $url');
});
}
});
_onScrollYChanged =
flutterWebviewPlugin.onScrollYChanged.listen((double y) {
if (mounted) {
setState(() {
_history.add("Scroll in Y Direction: $y");
});
}
});
_onScrollXChanged =
flutterWebviewPlugin.onScrollXChanged.listen((double x) {
if (mounted) {
setState(() {
_history.add("Scroll in X Direction: $x");
});
}
});
_onStateChanged =
flutterWebviewPlugin.onStateChanged.listen((WebViewStateChanged state) {
print('_onStateChanged');
if (mounted) {
setState(() {
_history.add('onStateChanged: ${state.type} ${state.url}');
});
}
});
_onHttpError =
flutterWebviewPlugin.onHttpError.listen((WebViewHttpError error) {
if (mounted) {
setState(() {
_history.add('onHttpError: ${error.code} ${error.url}');
});
}
});
}
@override
void dispose() {
// Every listener should be canceled, the same should be done with this stream.
_onDestroy.cancel();
_onUrlChanged.cancel();
_onStateChanged.cancel();
_onHttpError.cancel();
_onScrollXChanged.cancel();
_onScrollYChanged.cancel();
flutterWebviewPlugin.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Container(
padding: const EdgeInsets.all(24.0),
child: new TextField(controller: _urlCtrl),
),
new RaisedButton(
onPressed: () {
flutterWebviewPlugin.launch(selectedUrl,
rect: new Rect.fromLTWH(
0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
userAgent: kAndroidUserAgent);
},
child: const Text('Open Webview (rect)'),
),
new RaisedButton(
onPressed: () {
flutterWebviewPlugin.launch(selectedUrl, hidden: true);
},
child: const Text('Open "hidden" Webview'),
),
new RaisedButton(
onPressed: () {
flutterWebviewPlugin.launch(selectedUrl);
},
child: const Text('Open Fullscreen Webview'),
),
new RaisedButton(
onPressed: () {
flutterWebviewPlugin.evalJavascript(_codeCtrl.text);
Navigator.of(context).pushNamed('/widget');
},
child: const Text('Open widget webview'),
),
new Container(
padding: const EdgeInsets.all(24.0),
child: new TextField(controller: _codeCtrl),
),
new RaisedButton(
onPressed: () {
final future =
flutterWebviewPlugin.evalJavascript(_codeCtrl.text);
future.then((String result) {
setState(() {
_history.add('eval: $result');
print('eval: $result');
});
});
},
child: const Text('Eval some javascript'),
),
new RaisedButton(
onPressed: () {
setState(() {
_history.clear();
});
flutterWebviewPlugin.close();
},
child: const Text('Close'),
),
new RaisedButton(
onPressed: () {
flutterWebviewPlugin.getCookies().then((m) {
setState(() {
_history.add('cookies: $m');
});
});
},
child: const Text('Cookies'),
),
new Text(_history.join('\n'))
],
),
);
}
}
@brendan-duncan any updates regarding this issue?
Sorry, I forgot to get back to this.
I haven't used Flutter and haven't done any Android development, so my ability to test my hypothesis is limited. That error indicates it can't find the byte tag in the file marking the end of the zip. Looking at your code, my suspicion is that because you do the following:
File(path).copy(ffile.path);
List<int> bytes = new File(ffile.path).readAsBytesSync();
File.copy is async, returns a Future, which means the next line that tries to read that copied file, it hasn't finished copying yet so it can't find the end marker. I would either wait for the future to complete before doing the rest of the decode, or use File.copySync.
@brendan-duncan this my download function, which downloads the zip file from the Firebase Storage
it works but time to time getting the error
Future<bool> downloadAssetBucket(
{@required String categoryCode, @required firebaseConfig}) async {
try {
await _init(firebaseConfig: firebaseConfig);
final Directory systemTempDir = Directory.systemTemp;
// created theory directory if doesn't exists
final Directory theoryDir = Directory('${systemTempDir.path}$theoryDirectoryName');
if (!theoryDir.existsSync()) {
await theoryDir.create();
}
// created category directory if doesn't exists in theory directory
final Directory categoryDir = Directory('${theoryDir.path}${categoryCode}');
if (!categoryDir.existsSync()) {
await categoryDir.create();
}
final archiveName = "$categoryCode.$zipExtensionName";
final StorageReference storageReference =
await firebaseStorage.ref().child('$archiveBucketPath/$archiveName');
final File assetsArchive = File('${categoryDir.path}/$archiveName');
if (!assetsArchive.existsSync()) {
await assetsArchive.create();
}
final StorageFileDownloadTask task = await storageReference.writeToFile(assetsArchive);
await task;
// Read the Zip file from cache.
List<int> bytes = await assetsArchive.readAsBytes();
// Decode the Zip file
Archive archive = new ZipDecoder().decodeBytes(bytes);
// Extract the contents of the Zip archive to theory directory.
for (ArchiveFile file in archive) {
String filename = file.name;
String assetPath = '${theoryDir.path}/$filename';
if (file.isFile) {
List<int> data = file.content;
new File(assetPath)
..createSync(recursive: true)
..writeAsBytesSync(data);
}
}
//remove the zip file after decoding
assetsArchive.delete(recursive: true);
return true;
} catch (e) {
rethrow;
}
}
I don't see anything that stands out as obvious, so I'm not sure. The missing EOCD tag means it's not considered to be a valid zip, and if you know it's a valid zip, then something went wrong when reading the file, as in the first reported error. This is essentially what it's doing to find the EOCD tag in the zip file:
for (int i = bytes.length - 4; i >= 0; --i) {
// ECOD = 0x06054b50
if (bytes[i + 3] == 0x06 && bytes[i + 2] == 0x05 && bytes[i + 1] == 0x4b && bytes[i + 0] == 0x50) {
print("EOCD FOUND!");
break;
}
}
If that isn't found, it's not a valid zip file.
@brendan-duncan the file is zip and time to time it is downloading properly.
Sorry, not much I can do to help with that. Sounds like a timing issue, like the file sometimes hasn't finished writing. Good luck.
I just encountered the same error. Any updates?
So no fix for this one yet?
I got the same error when using latest version with decodeBuffer. To verify the issue, download sqlite zip file from https://sqlite.org/2021/sqlite-amalgamation-3360000.zip and then create Archive with decodeBuffer.
Update: using decodeBytes work perfectly.
How are you calling decodeBuffer? decodeBytes is the same as decodeBuffer(InputStream(bytes)).
same issue, any update on this?