mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-06-10 23:59:42 +08:00
android asset updates
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package com.microsoft.codepush.react;
|
||||
|
||||
public class CodePushInvalidPackageException extends RuntimeException {
|
||||
public CodePushInvalidPackageException() {
|
||||
super("Update is invalid - no files with extension .jsbundle or .bundle were found in the update package.");
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,14 @@ package com.microsoft.codepush.react;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
@@ -14,18 +18,23 @@ import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class CodePushPackage {
|
||||
|
||||
public final String CODE_PUSH_FOLDER_PREFIX = "CodePush";
|
||||
public final String STATUS_FILE = "codepush.json";
|
||||
public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle";
|
||||
public final String CURRENT_PACKAGE_KEY = "currentPackage";
|
||||
public final String PREVIOUS_PACKAGE_KEY = "previousPackage";
|
||||
public final String DIFF_MANIFEST_FILE_NAME = "hotcodepush.json";
|
||||
public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256;
|
||||
public final String DOWNLOAD_FILE_NAME = "download.zip";
|
||||
public final String DOWNLOAD_URL_KEY = "downloadUrl";
|
||||
public final String PACKAGE_FILE_NAME = "app.json";
|
||||
public final String PACKAGE_HASH_KEY = "packageHash";
|
||||
public final String DOWNLOAD_URL_KEY = "downloadUrl";
|
||||
public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256;
|
||||
public final String PREVIOUS_PACKAGE_KEY = "previousPackage";
|
||||
public final String RELATIVE_BUNDLE_PATH_KEY = "bundlePath";
|
||||
public final String STATUS_FILE = "codepush.json";
|
||||
public final String UNZIPPED_FOLDER_NAME = "unzipped";
|
||||
public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle";
|
||||
|
||||
private String documentsDirectory;
|
||||
|
||||
@@ -33,6 +42,14 @@ public class CodePushPackage {
|
||||
this.documentsDirectory = documentsDirectory;
|
||||
}
|
||||
|
||||
public String getDownloadFilePath() {
|
||||
return CodePushUtils.appendPathComponent(getCodePushPath(), DOWNLOAD_FILE_NAME);
|
||||
}
|
||||
|
||||
public String getUnzippedFolderPath() {
|
||||
return CodePushUtils.appendPathComponent(getCodePushPath(), UNZIPPED_FOLDER_NAME);
|
||||
}
|
||||
|
||||
public String getDocumentsDirectory() {
|
||||
return documentsDirectory;
|
||||
}
|
||||
@@ -87,7 +104,13 @@ public class CodePushPackage {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CodePushUtils.appendPathComponent(packageFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
WritableMap currentPackage = getCurrentPackage();
|
||||
String relativeBundlePath = CodePushUtils.tryGetString(currentPackage, RELATIVE_BUNDLE_PATH_KEY);
|
||||
if (relativeBundlePath == null) {
|
||||
return CodePushUtils.appendPathComponent(packageFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
} else {
|
||||
return CodePushUtils.appendPathComponent(packageFolder, relativeBundlePath);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPackageFolderPath(String packageHash) {
|
||||
@@ -132,7 +155,7 @@ public class CodePushPackage {
|
||||
public void downloadPackage(Context applicationContext, ReadableMap updatePackage,
|
||||
DownloadProgressCallback progressCallback) throws IOException {
|
||||
|
||||
String packageFolderPath = getPackageFolderPath(CodePushUtils.tryGetString(updatePackage, PACKAGE_HASH_KEY));
|
||||
String newPackageFolderPath = getPackageFolderPath(CodePushUtils.tryGetString(updatePackage, PACKAGE_HASH_KEY));
|
||||
String downloadUrlString = CodePushUtils.tryGetString(updatePackage, DOWNLOAD_URL_KEY);
|
||||
|
||||
URL downloadUrl = null;
|
||||
@@ -140,6 +163,8 @@ public class CodePushPackage {
|
||||
BufferedInputStream bin = null;
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream bout = null;
|
||||
File downloadFile = null;
|
||||
boolean isZip = false;
|
||||
|
||||
try {
|
||||
downloadUrl = new URL(downloadUrlString);
|
||||
@@ -149,23 +174,34 @@ public class CodePushPackage {
|
||||
long receivedBytes = 0;
|
||||
|
||||
bin = new BufferedInputStream(connection.getInputStream());
|
||||
File downloadFolder = new File(packageFolderPath);
|
||||
File downloadFolder = new File(getCodePushPath());
|
||||
downloadFolder.mkdirs();
|
||||
File downloadFile = new File(downloadFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
downloadFile = new File(downloadFolder, DOWNLOAD_FILE_NAME);
|
||||
fos = new FileOutputStream(downloadFile);
|
||||
bout = new BufferedOutputStream(fos, DOWNLOAD_BUFFER_SIZE);
|
||||
byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
|
||||
byte[] header = new byte[4];
|
||||
|
||||
int numBytesRead = 0;
|
||||
while ((numBytesRead = bin.read(data, 0, DOWNLOAD_BUFFER_SIZE)) >= 0) {
|
||||
if (receivedBytes < 4) {
|
||||
for (int i = 0; i < numBytesRead; i++) {
|
||||
int headerOffset = (int)(receivedBytes) + i;
|
||||
if (headerOffset >= 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
header[headerOffset] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
receivedBytes += numBytesRead;
|
||||
bout.write(data, 0, numBytesRead);
|
||||
progressCallback.call(new DownloadProgress(totalBytes, receivedBytes));
|
||||
}
|
||||
|
||||
assert totalBytes == receivedBytes;
|
||||
|
||||
String bundlePath = CodePushUtils.appendPathComponent(packageFolderPath, PACKAGE_FILE_NAME);
|
||||
CodePushUtils.writeReadableMapToFile(updatePackage, bundlePath);
|
||||
isZip = ByteBuffer.wrap(header).getInt() == 0x504b0304;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new CodePushMalformedDataException(downloadUrlString, e);
|
||||
} finally {
|
||||
@@ -178,6 +214,78 @@ public class CodePushPackage {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isZip) {
|
||||
System.err.println("THIS IS A ZIP!");
|
||||
String unzippedFolderPath = getUnzippedFolderPath();
|
||||
CodePushUtils.unzipFile(downloadFile, unzippedFolderPath);
|
||||
CodePushUtils.deleteFileSilently(downloadFile);
|
||||
String diffManifestFilePath = CodePushUtils.appendPathComponent(unzippedFolderPath,
|
||||
DIFF_MANIFEST_FILE_NAME);
|
||||
File diffManifestFile = new File(unzippedFolderPath, DIFF_MANIFEST_FILE_NAME);
|
||||
if (diffManifestFile.exists()) {
|
||||
String currentPackageFolderPath = getCurrentPackageFolderPath();
|
||||
CodePushUtils.mergeEntriesInFolder(currentPackageFolderPath, newPackageFolderPath);
|
||||
WritableMap diffManifest = CodePushUtils.getWritableMapFromFile(diffManifestFilePath);
|
||||
ReadableArray deletedFiles = diffManifest.getArray("deletedFiles");
|
||||
for (int i = 0; i < deletedFiles.size(); i++) {
|
||||
String fileNameToDelete = deletedFiles.getString(i);
|
||||
File fileToDelete = new File(newPackageFolderPath, fileNameToDelete);
|
||||
CodePushUtils.deleteFileSilently(fileToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
CodePushUtils.mergeEntriesInFolder(unzippedFolderPath, newPackageFolderPath);
|
||||
CodePushUtils.deleteFileAtPathSilently(unzippedFolderPath);
|
||||
String relativeBundlePath = findMainBundleInFolder(newPackageFolderPath);
|
||||
|
||||
if (relativeBundlePath == null) {
|
||||
throw new CodePushInvalidPackageException();
|
||||
} else {
|
||||
JSONObject updatePackageJSON = CodePushUtils.convertReadableToJsonObject(updatePackage);
|
||||
try {
|
||||
updatePackageJSON.put(RELATIVE_BUNDLE_PATH_KEY, relativeBundlePath);
|
||||
} catch (JSONException e) {
|
||||
throw new CodePushUnknownException("Unable to set key " +
|
||||
RELATIVE_BUNDLE_PATH_KEY + " to value " + relativeBundlePath +
|
||||
" in update package.", e);
|
||||
}
|
||||
updatePackage = CodePushUtils.convertJsonObjectToWriteable(updatePackageJSON);
|
||||
}
|
||||
} else {
|
||||
System.err.println("THIS IS NOT A ZIP!");
|
||||
// File is not a zip.
|
||||
File updateBundleFile = new File(newPackageFolderPath, UPDATE_BUNDLE_FILE_NAME);
|
||||
downloadFile.renameTo(updateBundleFile);
|
||||
}
|
||||
|
||||
String bundlePath = CodePushUtils.appendPathComponent(newPackageFolderPath, PACKAGE_FILE_NAME);
|
||||
CodePushUtils.writeReadableMapToFile(updatePackage, bundlePath);
|
||||
}
|
||||
|
||||
public String findMainBundleInFolder(String folderPath) {
|
||||
File folder = new File(folderPath);
|
||||
File[] folderFiles = folder.listFiles();
|
||||
for (File file : folderFiles) {
|
||||
String fullFilePath = CodePushUtils.appendPathComponent(folderPath, file.getName());
|
||||
if (file.isDirectory()) {
|
||||
String mainBundlePathInSubFolder = findMainBundleInFolder(fullFilePath);
|
||||
if (mainBundlePathInSubFolder != null) {
|
||||
return CodePushUtils.appendPathComponent(file.getName(), mainBundlePathInSubFolder);
|
||||
}
|
||||
} else {
|
||||
String fileName = file.getName();
|
||||
int dotIndex = fileName.lastIndexOf(".");
|
||||
if (dotIndex >= 0) {
|
||||
String fileExtension = fileName.substring(dotIndex + 1);
|
||||
if (fileExtension.equals("bundle") || fileExtension.equals("js") || fileExtension.equals("jsbundle")) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void installPackage(ReadableMap updatePackage) throws IOException {
|
||||
|
||||
@@ -15,17 +15,23 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class CodePushUtils {
|
||||
|
||||
public static final String CODE_PUSH_TAG = "CodePush";
|
||||
public static final String REACT_NATIVE_LOG_TAG = "ReactNative";
|
||||
public static final int WRITE_BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
public static String appendPathComponent(String basePath, String appendPathComponent) {
|
||||
return new File(basePath, appendPathComponent).getAbsolutePath();
|
||||
@@ -265,6 +271,108 @@ public class CodePushUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void unzipFile(File zipFile, String destination) throws IOException {
|
||||
FileInputStream fis = null;
|
||||
BufferedInputStream bis = null;
|
||||
ZipInputStream zis = null;
|
||||
try {
|
||||
fis = new FileInputStream(zipFile);
|
||||
bis = new BufferedInputStream(fis);
|
||||
zis = new ZipInputStream(bis);
|
||||
ZipEntry entry;
|
||||
|
||||
File destinationFolder = new File(destination);
|
||||
if (!destinationFolder.exists()) {
|
||||
destinationFolder.mkdirs();
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
String fileName = entry.getName();
|
||||
File file = new File(destinationFolder, fileName);
|
||||
if (entry.isDirectory()) {
|
||||
file.mkdirs();
|
||||
} else {
|
||||
File parent = file.getParentFile();
|
||||
if (!parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
FileOutputStream fout = new FileOutputStream(file);
|
||||
try {
|
||||
int numBytesRead;
|
||||
while ((numBytesRead = zis.read(buffer)) != -1) {
|
||||
fout.write(buffer, 0, numBytesRead);
|
||||
}
|
||||
} finally {
|
||||
fout.close();
|
||||
}
|
||||
}
|
||||
long time = entry.getTime();
|
||||
if (time > 0) {
|
||||
file.setLastModified(time);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (zis != null) zis.close();
|
||||
if (bis != null) bis.close();
|
||||
if (fis != null) fis.close();
|
||||
} catch (IOException e) {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void mergeEntriesInFolder(String fromPath, String destinationPath) throws IOException {
|
||||
File fromDir = new File(fromPath);
|
||||
File destDir = new File(destinationPath);
|
||||
if (!destDir.exists()) {
|
||||
destDir.mkdir();
|
||||
}
|
||||
|
||||
for (File fromFile : fromDir.listFiles()) {
|
||||
if (fromFile.isDirectory()) {
|
||||
mergeEntriesInFolder(
|
||||
CodePushUtils.appendPathComponent(fromPath, fromFile.getName()),
|
||||
CodePushUtils.appendPathComponent(destinationPath, fromFile.getName()));
|
||||
} else {
|
||||
File destFile = new File(destDir, fromFile.getName());
|
||||
FileInputStream fromFileStream = null;
|
||||
BufferedInputStream fromBufferedStream = null;
|
||||
FileOutputStream destStream = null;
|
||||
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
|
||||
try {
|
||||
fromFileStream = new FileInputStream(fromFile);
|
||||
fromBufferedStream = new BufferedInputStream(fromFileStream);
|
||||
destStream = new FileOutputStream(destFile);
|
||||
int bytesRead;
|
||||
while ((bytesRead = fromBufferedStream.read(buffer)) > 0) {
|
||||
destStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (fromFileStream != null) fromFileStream.close();
|
||||
if (fromBufferedStream != null) fromBufferedStream.close();
|
||||
if (destStream != null) destStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteFileAtPathSilently(String path) {
|
||||
deleteFileSilently(new File(path));
|
||||
}
|
||||
|
||||
public static void deleteFileSilently(File file) {
|
||||
if (!file.delete()) {
|
||||
Log.e(CODE_PUSH_TAG, "Error deleting file " + file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(String message) {
|
||||
Log.d(REACT_NATIVE_LOG_TAG, "[CodePush] " + message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user