公司临时分配了一个分发平台的项目,需要对上传的安装包进行解析安装包,并把数据存到数据库和七牛云,在此期间,我踩了非常多的坑,今天总结一下我踩了哪些坑。下面直接进入主题
1.首先我们要新建一个类库 ApkParser 我这里只做了apk的类库,ipa的以后有时间了再做
2.在类库下新建文件夹Zipper 后在此文件夹下添加UnzipApk 这个类,下面是代码
using System;
using System.IO;
using Ionic.Zip;
namespace APKParser.Zipper
{
internal class UnzipApk
{
public void UnzipApkFile(String filePath)
{
try
{
var zipFileDirectory = Path.GetDirectoryName(filePath);
var zipExtractFileDirectory = zipFileDirectory + "\\" + Path.GetFileNameWithoutExtension(filePath);
// If exists then delete folder
if (Directory.Exists(zipExtractFileDirectory))
{
Directory.Delete(zipExtractFileDirectory, true);
}
// Extract the zip
using (ZipFile zip = ZipFile.Read(filePath))
{
foreach (ZipEntry e in zip)
{
e.Extract(zipExtractFileDirectory, ExtractExistingFileAction.OverwriteSilently);
}
}
}
catch (Exception x)
{
Console.WriteLine("Error in extracting the APK file, Please consider the below Message.");
Console.WriteLine(x.StackTrace);
}
}
}
}
3.然后再在类库下新建 APKInfo 这个类 下面是代码
using System;
using System.Collections.Generic;
using System.Globalization;
namespace ApkParser.Model
{
public class ApkInfo
{
public static int FINE = 0;
public static int NULL_VERSION_CODE = 1;
public static int NULL_VERSION_NAME = 2;
public static int NULL_PERMISSION = 3;
public static int NULL_ICON = 4;
public static int NULL_CERT_FILE = 5;
public static int BAD_CERT = 6;
public static int NULL_SF_FILE = 7;
public static int BAD_SF = 8;
public static int NULL_MANIFEST = 9;
public static int NULL_RESOURCES = 10;
public static int NULL_DEX = 13;
public static int NULL_METAINFO = 14;
public static int BAD_JAR = 11;
public static int BAD_READ_INFO = 12;
public static int NULL_FILE = 15;
public static int HAS_REF = 16;
public List<String> Permissions;
public bool hasIcon;
public List<String> iconFileName;
public List<String> iconFileNameToGet;
public List<String> iconHash;
public String label;
public Dictionary<String, String> layoutStrings;
public String minSdkVersion;
public String packageName;
public Dictionary<String, List<String>> resStrings;
public byte[] resourcesFileBytes;
public String resourcesFileName;
public bool supportAnyDensity;
public bool supportLargeScreens;
public bool supportNormalScreens;
public bool supportSmallScreens;
public String targetSdkVersion;
public String versionCode;
public String versionName;
public ApkInfo()
{
hasIcon = false;
supportSmallScreens = false;
supportNormalScreens = false;
supportLargeScreens = false;
supportAnyDensity = true;
versionCode = null;
versionName = null;
iconFileName = null;
iconFileNameToGet = null;
Permissions = new List<String>();
}
public static bool supportSmallScreen(byte[] dpi)
{
if (dpi[0] == 1)
return true;
return false;
}
public static bool supportNormalScreen(byte[] dpi)
{
if (dpi[1] == 1)
return true;
return false;
}
public static bool supportLargeScreen(byte[] dpi)
{
if (dpi[2] == 1)
return true;
return false;
}
//public byte[] getDPI()
//{
// byte[] dpi = new byte[3];
// if (this.supportAnyDensity)
// {
// dpi[0] = 1;
// dpi[1] = 1;
// dpi[2] = 1;
// }
// else
// {
// if (this.supportSmallScreens)
// dpi[0] = 1;
// if (this.supportNormalScreens)
// dpi[1] = 1;
// if (this.supportLargeScreens)
// dpi[2] = 1;
// }
// return dpi;
//}
private bool isReference(List<String> strs)
{
try
{
foreach (String str in strs)
{
if (isReference(str))
return true;
}
}
catch (Exception e)
{
throw e;
}
return false;
}
private bool isReference(String str)
{
try
{
if (str != null && str.StartsWith("@"))
{
int.Parse(str, NumberStyles.HexNumber);
return true;
}
}
catch (Exception e)
{
throw e;
}
return false;
}
}
}
4.再在类库里添加APKManifest 这个类 下面是代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace APKParser
{
public class ApkManifest
{
private string _result = "";
// decompressXML -- Parse the 'compressed' binary form of Android XML docs
// such as for AndroidManifest.xml in .apk files
public static int EndDocTag = 0x00100101;
public static int StartTag = 0x00100102;
public static int EndTag = 0x00100103;
public string ReadManifestFileIntoXml(byte[] manifestFileData)
{
if (manifestFileData.Length == 0)
throw new Exception("Failed to read manifest data. Byte array was empty");
// Compressed XML file/bytes starts with 24x bytes of data,
// 9 32 bit words in little endian order (LSB first):
// 0th word is 03 00 08 00
// 3rd word SEEMS TO BE: Offset at then of StringTable
// 4th word is: Number of strings in string table
// WARNING: Sometime I indiscriminently display or refer to word in
// little endian storage format, or in integer format (ie MSB first).
int numbStrings = LEW(manifestFileData, 4 * 4);
// StringIndexTable starts at offset 24x, an array of 32 bit LE offsets
// of the length/string data in the StringTable.
int sitOff = 0x24; // Offset of start of StringIndexTable
// StringTable, each string is represented with a 16 bit little endian
// character count, followed by that number of 16 bit (LE) (Unicode) chars.
int stOff = sitOff + numbStrings * 4; // StringTable follows StrIndexTable
// XMLTags, The XML tag tree starts after some unknown content after the
// StringTable. There is some unknown data after the StringTable, scan
// forward from this point to the flag for the start of an XML start tag.
int xmlTagOff = LEW(manifestFileData, 3 * 4); // Start from the offset in the 3rd word.
// Scan forward until we find the bytes: 0x02011000(x00100102 in normal int)
for (int ii = xmlTagOff; ii < manifestFileData.Length - 4; ii += 4)
{
if (LEW(manifestFileData, ii) == StartTag)
{
xmlTagOff = ii; break;
}
} // end of hack, scanning for start of first start tag
// XML tags and attributes:
// Every XML start and end tag consists of 6 32 bit words:
// 0th word: 02011000 for startTag and 03011000 for endTag
// 1st word: a flag?, like 38000000
// 2nd word: Line of where this tag appeared in the original source file
// 3rd word: FFFFFFFF ??
// 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS
// 5th word: StringIndex of Element Name
// (Note: 01011000 in 0th word means end of XML document, endDocTag)
// Start tags (not end tags) contain 3 more words:
// 6th word: 14001400 meaning??
// 7th word: Number of Attributes that follow this tag(follow word 8th)
// 8th word: 00000000 meaning??
// Attributes consist of 5 words:
// 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF
// 1st word: StringIndex of Attribute Name
// 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId used
// 3rd word: Flags?
// 4th word: str ind of attr value again, or ResourceId of value
// TMP, dump string table to tr for debugging
//tr.addSelect("strings", null);
//for (int ii=0; ii<numbStrings; ii++) {
// // Length of string starts at StringTable plus offset in StrIndTable
// String str = compXmlString(xml, sitOff, stOff, ii);
// tr.add(String.valueOf(ii), str);
//}
//tr.parent();
// Step through the XML tree element tags and attributes
int off = xmlTagOff;
int indent = 0;
int startTagLineNo = -2;
while (off < manifestFileData.Length)
{
int tag0 = LEW(manifestFileData, off);
//int tag1 = LEW(manifestFileData, off+1*4);
int lineNo = LEW(manifestFileData, off + 2 * 4);
//int tag3 = LEW(manifestFileData, off+3*4);
int nameNsSi = LEW(manifestFileData, off + 4 * 4);
int nameSi = LEW(manifestFileData, off + 5 * 4);
if (tag0 == StartTag)
{ // XML START TAG
int tag6 = LEW(manifestFileData, off + 6 * 4); // Expected to be 14001400
int numbAttrs = LEW(manifestFileData, off + 7 * 4); // Number of Attributes to follow
//int tag8 = LEW(manifestFileData, off+8*4); // Expected to be 00000000
off += 9 * 4; // Skip over 6+3 words of startTag data
String name = compXmlString(manifestFileData, sitOff, stOff, nameSi);
//tr.addSelect(name, null);
startTagLineNo = lineNo;
// Look for the Attributes
string sb = "";
for (int ii = 0; ii < numbAttrs; ii++)
{
int attrNameNsSi = LEW(manifestFileData, off); // AttrName Namespace Str Ind, or FFFFFFFF
int attrNameSi = LEW(manifestFileData, off + 1 * 4); // AttrName String Index
int attrValueSi = LEW(manifestFileData, off + 2 * 4); // AttrValue Str Ind, or FFFFFFFF
int attrFlags = LEW(manifestFileData, off + 3 * 4);
int attrResId = LEW(manifestFileData, off + 4 * 4); // AttrValue ResourceId or dup AttrValue StrInd
off += 5 * 4; // Skip over the 5 words of an attribute
String attrName = compXmlString(manifestFileData, sitOff, stOff, attrNameSi);
String attrValue = attrValueSi != -1
? compXmlString(manifestFileData, sitOff, stOff, attrValueSi)
: /*"resourceID 0x" + */attrResId.ToString();
sb += " " + attrName + "=\"" + attrValue + "\"";
//tr.add(attrName, attrValue);
}
prtIndent(indent, "<" + name + sb + ">");
indent++;
}
else if (tag0 == EndTag)
{ // XML END TAG
indent--;
off += 6 * 4; // Skip over 6 words of endTag data
String name = compXmlString(manifestFileData, sitOff, stOff, nameSi);
prtIndent(indent, "</" + name + "> \r\n"/*+"(line " + startTagLineNo + "-" + lineNo + ")"*/);
//tr.parent(); // Step back up the NobTree
}
else if (tag0 == EndDocTag)
{ // END OF XML DOC TAG
break;
}
else
{
prt(" Unrecognized tag code '" + tag0.ToString("X")
+ "' at offset " + off);
break;
}
} // end of while loop scanning tags and attributes of XML tree
//prt(" end at offset " + off);
return _result;
} // end of decompressXML
public String compXmlString(byte[] xml, int sitOff, int stOff, int strInd)
{
if (strInd < 0) return null;
int strOff = stOff + LEW(xml, sitOff + strInd * 4);
return compXmlStringAt(xml, strOff);
}
public static String spaces = " ";
public void prtIndent(int indent, String str)
{
prt(spaces.Substring(0, Math.Min(indent * 2, spaces.Length)) + str);
}
private void prt(string p)
{
_result += p;
}
// compXmlStringAt -- Return the string stored in StringTable format at
// offset strOff. This offset points to the 16 bit string length, which
// is followed by that number of 16 bit (Unicode) chars.
public String compXmlStringAt(byte[] arr, int strOff)
{
int strLen = arr[strOff + 1] << 8 & 0xff00 | arr[strOff] & 0xff;
byte[] chars = new byte[strLen];
for (int ii = 0; ii < strLen; ii++)
{
chars[ii] = arr[strOff + 2 + ii * 2];
}
return System.Text.Encoding.UTF8.GetString(chars); // Hack, just use 8 byte chars
} // end of compXmlStringAt
// LEW -- Return value of a Little Endian 32 bit word from the byte array
// at offset off.
public int LEW(byte[] arr, int off)
{
//return (int)(arr[off + 3] << 24 & 0xff000000 | arr[off + 2] << 16 & 0xff0000 | arr[off + 1] << 8 & 0xff00 | arr[off] & 0xFF);
return (int)(((uint)arr[off + 3]) << 24 & 0xff000000 | ((uint)arr[off + 2]) << 16 & 0xff0000 | ((uint)arr[off + 1]) << 8 & 0xff00 | ((uint)arr[off]) & 0xFF);
} // end of LEW
}
}
5.再在类库里添加ApkReader 这个类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using ApkParser.Model;
using ApkParser;
namespace APKParser
{
public class ApkReader
{
//private static Logger log = Logger.getLogger("APKReader");
private const int VER_ID = 0;
private const int ICN_ID = 1;
private const int LABEL_ID = 2;
String[] VER_ICN = new String[3];
// Some possible tags and attributes
String[] TAGS = { "manifest", "application", "activity" };
String[] ATTRS = { "android:", "a:", "activity:", "_:" };
Dictionary<String, object> entryList = new Dictionary<String, object>();
List<String> tmpFiles = new List<String>();
public String fuzzFindInDocument(XmlDocument doc, String tag, String attr)
{
foreach (String t in TAGS)
{
XmlNodeList nodelist = doc.GetElementsByTagName(t);
for (int i = 0; i < nodelist.Count; i++)
{
XmlNode element = (XmlNode)nodelist.Item(i);
if (element.NodeType == XmlNodeType.Element)
{
XmlAttributeCollection map = element.Attributes;
for (int j = 0; j < map.Count; j++)
{
XmlNode element2 = map.Item(j);
if (element2.Name.EndsWith(attr))
{
return element2.Value;
}
}
}
}
}
return null;
}
private XmlDocument initDoc(String xml)
{
XmlDocument retval = new XmlDocument();
retval.LoadXml(xml);
retval.DocumentElement.Normalize();
return retval;
}
private void extractPermissions(ApkInfo info, XmlDocument doc)
{
ExtractPermission(info, doc, "uses-permission", "name");
ExtractPermission(info, doc, "permission-group", "name");
ExtractPermission(info, doc, "service", "permission");
ExtractPermission(info, doc, "provider", "permission");
ExtractPermission(info, doc, "activity", "permission");
}
private bool readBoolean(XmlDocument doc, String tag, String attribute)
{
String str = FindInDocument(doc, tag, attribute);
bool ret = false;
try
{
ret = Convert.ToBoolean(str);
}
catch
{
ret = false;
}
return ret;
}
private void extractSupportScreens(ApkInfo info, XmlDocument doc)
{
info.supportSmallScreens = readBoolean(doc, "supports-screens", "android:smallScreens");
info.supportNormalScreens = readBoolean(doc, "supports-screens", "android:normalScreens");
info.supportLargeScreens = readBoolean(doc, "supports-screens", "android:largeScreens");
if (info.supportSmallScreens || info.supportNormalScreens || info.supportLargeScreens)
info.supportAnyDensity = false;
}
public ApkInfo ExtractInfo(byte[] manifest_xml, byte[] resources_arsx)
{
string manifestXml;
var manifest = new ApkManifest();
try
{
manifestXml = manifest.ReadManifestFileIntoXml(manifest_xml);
}
catch (Exception ex)
{
throw ex;
}
var doc = new XmlDocument();
doc.LoadXml(manifestXml);
return ExtractInfo(doc, resources_arsx);
}
public ApkInfo ExtractInfo(XmlDocument manifestXml, byte[] resources_arsx)
{
var info = new ApkInfo();
VER_ICN[VER_ID] = "";
VER_ICN[ICN_ID] = "";
VER_ICN[LABEL_ID] = "";
try
{
XmlDocument doc = manifestXml;
if (doc == null)
throw new Exception("Document initialize failed");
info.resourcesFileName = "resources.arsx";
info.resourcesFileBytes = resources_arsx;
// Fill up the permission field
extractPermissions(info, doc);
// Fill up some basic fields
info.minSdkVersion = FindInDocument(doc, "uses-sdk", "minSdkVersion");
info.targetSdkVersion = FindInDocument(doc, "uses-sdk", "targetSdkVersion");
info.versionCode = FindInDocument(doc, "manifest", "versionCode");
info.versionName = FindInDocument(doc, "manifest", "versionName");
info.packageName = FindInDocument(doc, "manifest", "package");
info.label = FindInDocument(doc, "application", "label");
if (info.label.StartsWith("@"))
VER_ICN[LABEL_ID] = info.label;
else
VER_ICN[LABEL_ID] = String.Format("@{0}", Convert.ToInt32(info.label).ToString("X4"));
// Fill up the support screen field
extractSupportScreens(info, doc);
if (info.versionCode == null)
info.versionCode = fuzzFindInDocument(doc, "manifest",
"versionCode");
if (info.versionName == null)
info.versionName = fuzzFindInDocument(doc, "manifest",
"versionName");
else if (info.versionName.StartsWith("@"))
VER_ICN[VER_ID] = info.versionName;
String id = FindInDocument(doc, "application", "android:icon");
if (null == id)
{
id = fuzzFindInDocument(doc, "manifest", "icon");
}
if (null == id)
{
Debug.WriteLine("icon resId Not Found!");
return info;
}
// Find real strings
if (!info.hasIcon)
{
if (id.StartsWith("@android:"))
VER_ICN[ICN_ID] = "@"
+ (id.Substring("@android:".Length));
else
VER_ICN[ICN_ID] = String.Format("@{0}", Convert.ToInt32(id).ToString("X4"));
var resId = new List<String>();
for(int i = 0; i < VER_ICN.Length; i++)
{
if (VER_ICN[i].StartsWith("@"))
resId.Add(VER_ICN[i]);
}
var finder = new ApkResourceFinder();
info.resStrings = finder.processResourceTable(info.resourcesFileBytes, resId);
if (!VER_ICN[VER_ID].Equals(""))
{
List<String> versions = null;
if (info.resStrings.ContainsKey(VER_ICN[VER_ID].ToUpper()))
versions = info.resStrings[VER_ICN[VER_ID].ToUpper()];
if (versions != null)
{
if (versions.Count > 0)
info.versionName = versions[0];
}
else
{
throw new Exception(
"VersionName Cant Find in resource with id "
+ VER_ICN[VER_ID]);
}
}
List<String> iconPaths = null;
if (info.resStrings.ContainsKey(VER_ICN[ICN_ID].ToUpper()))
iconPaths = info.resStrings[VER_ICN[ICN_ID].ToUpper()];
if (iconPaths != null && iconPaths.Count > 0)
{
info.iconFileNameToGet = new List<String>();
info.iconFileName = new List<string>();
foreach (String iconFileName in iconPaths)
{
if (iconFileName != null)
{
if (iconFileName.Contains(@"/"))
{
info.iconFileNameToGet.Add(iconFileName);
info.iconFileName.Add(iconFileName);
info.hasIcon = true;
}
}
}
}
else
{
throw new Exception("Icon Cant Find in resource with id "
+ VER_ICN[ICN_ID]);
}
if (!VER_ICN[LABEL_ID].Equals(""))
{
List<String> labels = null;
if (info.resStrings.ContainsKey(VER_ICN[LABEL_ID]))
labels = info.resStrings[VER_ICN[LABEL_ID]];
if (labels != null && labels.Count > 0)
{
info.label = labels[0];
}
}
}
}
catch (Exception e)
{
throw e;
}
return info;
}
private void ExtractPermission(ApkInfo info, XmlDocument doc, String keyName, String attribName)
{
XmlNodeList usesPermissions = doc.GetElementsByTagName(keyName);
if (usesPermissions != null)
{
for (int s = 0; s < usesPermissions.Count; s++)
{
XmlNode permissionNode = usesPermissions.Item(s);
if (permissionNode != null && permissionNode.NodeType == XmlNodeType.Element)
{
XmlNode node = permissionNode.Attributes.GetNamedItem(attribName);
if (node != null)
info.Permissions.Add(node.Value);
}
}
}
}
private String FindInDocument(XmlDocument doc, String keyName,
String attribName)
{
XmlNodeList usesPermissions = doc.GetElementsByTagName(keyName);
if (usesPermissions != null)
{
for (int s = 0; s < usesPermissions.Count; s++)
{
XmlNode permissionNode = usesPermissions.Item(s);
if (permissionNode.NodeType == XmlNodeType.Element)
{
XmlNode node = permissionNode.Attributes.GetNamedItem(attribName);
if (node != null)
return node.Value;
}
}
}
return null;
}
}
}
6.再在类库里添加ApkResourceFinder 这个类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace ApkParser
{
public class ApkResourceFinder
{
private const long HEADER_START = 0;
static short RES_STRING_POOL_TYPE = 0x0001;
static short RES_TABLE_TYPE = 0x0002;
static short RES_TABLE_PACKAGE_TYPE = 0x0200;
static short RES_TABLE_TYPE_TYPE = 0x0201;
static short RES_TABLE_TYPE_SPEC_TYPE = 0x0202;
String[] valueStringPool = null;
String[] typeStringPool = null;
String[] keyStringPool = null;
private int package_id = 0;
private List<String> resIdList;
Contains no data.
//static byte TYPE_NULL = 0x00;
The 'data' holds an attribute resource identifier.
//static byte TYPE_ATTRIBUTE = 0x02;
The 'data' holds a single-precision floating point number.
//static byte TYPE_FLOAT = 0x04;
The 'data' holds a complex number encoding a dimension value,
such as "100in".
//static byte TYPE_DIMENSION = 0x05;
The 'data' holds a complex number encoding a fraction of a
container.
//static byte TYPE_FRACTION = 0x06;
The 'data' is a raw integer value of the form n..n.
//static byte TYPE_INT_DEC = 0x10;
The 'data' is a raw integer value of the form 0xn..n.
//static byte TYPE_INT_HEX = 0x11;
The 'data' is either 0 or 1, for input "false" or "true" respectively.
//static byte TYPE_INT_BOOLEAN = 0x12;
The 'data' is a raw integer value of the form #aarrggbb.
//static byte TYPE_INT_COLOR_ARGB8 = 0x1c;
The 'data' is a raw integer value of the form #rrggbb.
//static byte TYPE_INT_COLOR_RGB8 = 0x1d;
The 'data' is a raw integer value of the form #argb.
//static byte TYPE_INT_COLOR_ARGB4 = 0x1e;
The 'data' is a raw integer value of the form #rgb.
//static byte TYPE_INT_COLOR_RGB4 = 0x1f;
// The 'data' holds a ResTable_ref, a reference to another resource
// table entry.
static byte TYPE_REFERENCE = 0x01;
// The 'data' holds an index into the containing resource table's
// global value string pool.
static byte TYPE_STRING = 0x03;
private Dictionary<String, List<String>> responseMap;
Dictionary<int, List<String>> entryMap = new Dictionary<int, List<String>>();
public Dictionary<string, List<String>> initialize()
{
byte[] data = System.IO.File.ReadAllBytes("resources.arsc");
return this.processResourceTable(data, new List<string>());
}
public Dictionary<string, List<String>> processResourceTable(byte[] data, List<String> resIdList)
{
this.resIdList = resIdList;
responseMap = new Dictionary<string, List<String>>();
long lastPosition;
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
short type = br.ReadInt16();
short headerSize = br.ReadInt16();
int size = br.ReadInt32();
int packageCount = br.ReadInt32();
if (type != RES_TABLE_TYPE)
{
throw new Exception("No RES_TABLE_TYPE found!");
}
if (size != br.BaseStream.Length)
{
throw new Exception("The buffer size not matches to the resource table size.");
}
int realStringPoolCount = 0;
int realPackageCount = 0;
while (true)
{
long pos = br.BaseStream.Position;
short t = br.ReadInt16();
short hs = br.ReadInt16();
int s = br.ReadInt32();
if (t == RES_STRING_POOL_TYPE)
{
if (realStringPoolCount == 0)
{
// Only the first string pool is processed.
Debug.WriteLine("Processing the string pool ...");
byte[] buffer = new byte[s];
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(pos, SeekOrigin.Begin);
buffer = br.ReadBytes(s);
//br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
valueStringPool = processStringPool(buffer);
}
realStringPoolCount++;
}
else if (t == RES_TABLE_PACKAGE_TYPE)
{
// Process the package
Debug.WriteLine("Processing package {0} ...", realPackageCount);
byte[] buffer = new byte[s];
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(pos, SeekOrigin.Begin);
buffer = br.ReadBytes(s);
//br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
processPackage(buffer);
realPackageCount++;
}
else
{
throw new InvalidOperationException("Unsupported Type");
}
br.BaseStream.Seek(pos + (long)s, SeekOrigin.Begin);
if (br.BaseStream.Position == br.BaseStream.Length)
break;
}
if (realStringPoolCount != 1)
{
throw new Exception("More than 1 string pool found!");
}
if (realPackageCount != packageCount)
{
throw new Exception(
"Real package count not equals the declared count.");
}
return responseMap;
}
}
}
private void processPackage(byte[] data)
{
long lastPosition = 0;
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
//HEADER
short type = br.ReadInt16();
short headerSize = br.ReadInt16();
int size = br.ReadInt32();
int id = br.ReadInt32();
package_id = id;
//PackageName
char[] name = new char[256];
for (int i = 0; i < 256; ++i)
{
name[i] = br.ReadChar();
}
int typeStrings = br.ReadInt32();
int lastPublicType = br.ReadInt32();
int keyStrings = br.ReadInt32();
int lastPublicKey = br.ReadInt32();
if (typeStrings != headerSize)
{
throw new Exception("TypeStrings must immediately follow the package structure header.");
}
Debug.WriteLine("Type strings:");
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(typeStrings, SeekOrigin.Begin);
byte[] bbTypeStrings = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
typeStringPool = processStringPool(bbTypeStrings);
Debug.WriteLine("Key strings:");
br.BaseStream.Seek(keyStrings, SeekOrigin.Begin);
short key_type = br.ReadInt16();
short key_headerSize = br.ReadInt16();
int key_size = br.ReadInt32();
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(keyStrings, SeekOrigin.Begin);
byte[] bbKeyStrings = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
keyStringPool = processStringPool(bbKeyStrings);
// Iterate through all chunks
//
int typeSpecCount = 0;
int typeCount = 0;
br.BaseStream.Seek((keyStrings + key_size), SeekOrigin.Begin);
while (true)
{
int pos = (int)br.BaseStream.Position;
short t = br.ReadInt16();
short hs = br.ReadInt16();
int s = br.ReadInt32();
if (t == RES_TABLE_TYPE_SPEC_TYPE)
{
// Process the string pool
byte[] buffer = new byte[s];
br.BaseStream.Seek(pos, SeekOrigin.Begin);
buffer = br.ReadBytes(s);
processTypeSpec(buffer);
typeSpecCount++;
}
else if (t == RES_TABLE_TYPE_TYPE)
{
// Process the package
byte[] buffer = new byte[s];
br.BaseStream.Seek(pos, SeekOrigin.Begin);
buffer = br.ReadBytes(s);
processType(buffer);
typeCount++;
}
br.BaseStream.Seek(pos + s, SeekOrigin.Begin);
if (br.BaseStream.Position == br.BaseStream.Length)
break;
}
return;
}
}
}
private void putIntoMap(String resId, String value)
{
List<String> valueList = null;
if (responseMap.ContainsKey(resId.ToUpper()))
valueList = responseMap[resId.ToUpper()];
if (valueList == null)
{
valueList = new List<String>();
}
valueList.Add(value);
if (responseMap.ContainsKey(resId.ToUpper()))
responseMap[resId.ToUpper()] = valueList;
else
responseMap.Add(resId.ToUpper(), valueList);
return;
}
private void processType(byte[] typeData)
{
using (MemoryStream ms = new MemoryStream(typeData))
{
using (BinaryReader br = new BinaryReader(ms))
{
short type = br.ReadInt16();
short headerSize = br.ReadInt16();
int size = br.ReadInt32();
byte id = br.ReadByte();
byte res0 = br.ReadByte();
short res1 = br.ReadInt16();
int entryCount = br.ReadInt32();
int entriesStart = br.ReadInt32();
Dictionary<String, int> refKeys = new Dictionary<String, int>();
int config_size = br.ReadInt32();
// Skip the config data
br.BaseStream.Seek(headerSize, SeekOrigin.Begin);
if (headerSize + entryCount * 4 != entriesStart)
{
throw new Exception("HeaderSize, entryCount and entriesStart are not valid.");
}
// Start to get entry indices
int[] entryIndices = new int[entryCount];
for (int i = 0; i < entryCount; ++i)
{
entryIndices[i] = br.ReadInt32();
}
// Get entries
for (int i = 0; i < entryCount; ++i)
{
if (entryIndices[i] == -1)
continue;
int resource_id = (package_id << 24) | (id << 16) | i;
long pos = br.BaseStream.Position;
short entry_size = br.ReadInt16();
short entry_flag = br.ReadInt16();
int entry_key = br.ReadInt32();
// Get the value (simple) or map (complex)
int FLAG_COMPLEX = 0x0001;
if ((entry_flag & FLAG_COMPLEX) == 0)
{
// Simple case
short value_size = br.ReadInt16();
byte value_res0 = br.ReadByte();
byte value_dataType = br.ReadByte();
int value_data = br.ReadInt32();
String idStr = resource_id.ToString("X4");
String keyStr = keyStringPool[entry_key];
String data = null;
Debug.WriteLine("Entry 0x" + idStr + ", key: " + keyStr + ", simple value type: ");
List<String> entryArr = null;
if (entryMap.ContainsKey(int.Parse(idStr, System.Globalization.NumberStyles.HexNumber)))
entryArr = entryMap[int.Parse(idStr, System.Globalization.NumberStyles.HexNumber)];
if (entryArr == null)
entryArr = new List<String>();
entryArr.Add(keyStr);
if (entryMap.ContainsKey(int.Parse(idStr, System.Globalization.NumberStyles.HexNumber)))
entryMap[int.Parse(idStr, System.Globalization.NumberStyles.HexNumber)] = entryArr;
else
entryMap.Add(int.Parse(idStr, System.Globalization.NumberStyles.HexNumber), entryArr);
if (value_dataType == TYPE_STRING)
{
data = valueStringPool[value_data];
Debug.WriteLine(", data: " + valueStringPool[value_data] + "");
}
else if (value_dataType == TYPE_REFERENCE)
{
String hexIndex = value_data.ToString("X4");
refKeys.Add(idStr, value_data);
}
else
{
data = value_data.ToString();
Debug.WriteLine(", data: " + value_data + "");
}
// if (inReqList("@" + idStr)) {
putIntoMap("@" + idStr, data);
}
else
{
int entry_parent = br.ReadInt32();
int entry_count = br.ReadInt32();
for (int j = 0; j < entry_count; ++j)
{
int ref_name = br.ReadInt32();
short value_size = br.ReadInt16();
byte value_res0 = br.ReadByte();
byte value_dataType = br.ReadByte();
int value_data = br.ReadInt32();
}
Debug.WriteLine("Entry 0x"
+ resource_id.ToString("X4") + ", key: "
+ keyStringPool[entry_key]
+ ", complex value, not printed.");
}
}
HashSet<String> refKs = new HashSet<String>(refKeys.Keys);
foreach (String refK in refKs)
{
List<String> values = null;
if (responseMap.ContainsKey("@" + refKeys[refK].ToString("X4").ToUpper()))
values = responseMap["@" + refKeys[refK].ToString("X4").ToUpper()];
if (values != null)
foreach (String value in values)
{
putIntoMap("@" + refK, value);
}
}
return;
}
}
}
private string[] processStringPool(byte[] data)
{
long lastPosition = 0;
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
short type = br.ReadInt16();
short headerSize = br.ReadInt16();
int size = br.ReadInt32();
int stringCount = br.ReadInt32();
int styleCount = br.ReadInt32();
int flags = br.ReadInt32();
int stringsStart = br.ReadInt32();
int stylesStart = br.ReadInt32();
bool isUTF_8 = (flags & 256) != 0;
int[] offsets = new int[stringCount];
for (int i = 0; i < stringCount; ++i)
{
offsets[i] = br.ReadInt32();
}
String[] strings = new String[stringCount];
for (int i = 0; i < stringCount; i++)
{
int pos = stringsStart + offsets[i];
lastPosition = br.BaseStream.Position;
short len = (short)br.BaseStream.Seek(pos, SeekOrigin.Begin);
br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
if (len < 0)
{
short extendShort = br.ReadInt16();
}
pos += 2;
strings[i] = "";
if (isUTF_8)
{
int start = pos;
int length = 0;
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(pos, SeekOrigin.Begin);
while (br.ReadByte() != 0)
{
length++;
pos++;
}
br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
byte[] oneData = new byte[length];
if (length > 0)
{
byte[] byteArray = data;
for (int k = 0; k < length; k++)
{
oneData[k] = byteArray[start + k];
}
}
if (oneData.Length > 0)
strings[i] = Encoding.UTF8.GetString(oneData);
else
strings[i] = "";
}
else
{
char c;
lastPosition = br.BaseStream.Position;
br.BaseStream.Seek(pos, SeekOrigin.Begin);
while ((c = br.ReadChar()) != 0)
{
strings[i] += c;
pos += 2;
}
br.BaseStream.Seek(lastPosition, SeekOrigin.Begin);
}
Debug.WriteLine("Parsed value: {0}", strings[i]);
}
return strings;
}
}
}
private void processTypeSpec(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (BinaryReader br = new BinaryReader(ms))
{
short type = br.ReadInt16();
short headerSize = br.ReadInt16();
int size = br.ReadInt32();
byte id = br.ReadByte();
byte res0 = br.ReadByte();
short res1 = br.ReadInt16();
int entryCount = br.ReadInt32();
Debug.WriteLine("Processing type spec {0}", typeStringPool[id - 1]);
int[] flags = new int[entryCount];
for (int i = 0; i < entryCount; ++i)
{
flags[i] = br.ReadInt32();
}
return;
}
}
}
}
}
7.再在类库里添加ParseAPK 这个类
using System;
using System.IO;
using ApkParser.Model;
using APKParser.Zipper;
using ApkParser;
namespace APKParser
{
public class ParseApk
{
public ApkInfo ParseApkInfo(String filePath)
{
// Reading the APK File
var apkReader = new ApkReader();
//Unzipping the APK file to read the information from the APK file
var unzipApk = new UnzipApk();
unzipApk.UnzipApkFile(filePath);
//Variables will store the binary data
byte[] manifestData = null;
byte[] resourcesData = null;
// Looping through each and every sub directory in the extracted directory
String zipExtractFileDirectory = Path.GetDirectoryName(filePath) + "\\" +
Path.GetFileNameWithoutExtension(filePath);
try
{
foreach (string file in Directory.GetFiles(zipExtractFileDirectory))
{
var fileName = Path.GetFileName(file);
if (fileName != null && fileName.ToLower() == "androidmanifest.xml")
{
manifestData = new byte[50*1024];
using (var binaryStream = new BinaryReader(File.Open(file, FileMode.Open)))
{
manifestData = binaryStream.ReadBytes((int)binaryStream.BaseStream.Length);
}
}
else if (fileName != null && fileName.ToLower() == "resources.arsc")
{
using (var binaryStream = new BinaryReader(File.Open(file, FileMode.Open)))
{
resourcesData = binaryStream.ReadBytes((int)binaryStream.BaseStream.Length);
}
}
}
// Reading the manifest file and Resources file
var apkInfo = apkReader.ExtractInfo(manifestData, resourcesData);
return apkInfo;
}
catch (Exception excpt)
{
Console.WriteLine(excpt.Message);
return null;
}
}
}
}
下面给你们看一下这个类库的一些引用
好 到这里我们的条件就这么好了。下面开始我们就实操 上代码
string url= Server.MapPath("/Areas/Distribute/File/");//服务器图片存储位置
string saveurl = UploadLocal.LocalImage(HttpContext.Request.Files[0], url); //这里我将文件保存到本地,返回其路径地址
ApkInfo info = _applyList.GetApkInfo(saveurl);//这里就去使用我这个类库里的实际方法了,传入我存的文件路径 ,解析后返回我对应的安装包信息 这里很占用资源
string icon = url + System.IO.Path.GetFileNameWithoutExtension(saveurl) + "/" + info.iconFileName[0]; //这个是解析后的应用图标路径
icon = icon.Replace('/', '\\');//解决路径小毛病
string Qicon = await _applyList.UploadIcon(icon);//这里通过路径上传图标到七牛云
string Qurl = await _applyList.UploadData(saveurl);//这里上传安装包到七牛云
UploadLocal.DeleteDir(url);//删除指定文件夹下面所有文件和文件夹
下面那一节是我的业务操作就不展示了
我这里把存本地的方法也放出来
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace GenernalMVC.Common.Upload
{
public class UploadLocal
{
/// <summary>
/// 上传到服务器本地
/// </summary>
/// <param name="files">文件流</param>
/// <param name="url">文件存到哪个文件夹</param>
/// <returns></returns>
public static string LocalImage(HttpPostedFileBase files, string url)
{
MemoryStream target = new MemoryStream();
files.InputStream.CopyTo(target);
if (files == null)
{//判断是否空提交
return null;
}
string name = UploadImage.Chars(6, true, "");
string extension = Path.GetExtension(files.FileName).ToLower();
string fileName = name + extension;
string saveUrl = url + fileName;
files.SaveAs(saveUrl);//保存文件
return saveUrl;
}
#region 删除指定文件夹下的所有文件
/// <summary>
/// 删除指定文件夹下的所有文件
/// </summary>
/// <param name="strPath"></param>
/// <returns></returns>
public static bool DeleteDir(string strPath)
{
try
{
strPath = @strPath.Trim();
if (Directory.Exists(strPath))
{
string[] strDirs = Directory.GetDirectories(strPath);
string[] strFiles = Directory.GetFiles(strPath);
foreach (string strFile in strFiles)
{
File.Delete(strFile);
}
foreach (string strdir in strDirs)
{
Directory.Delete(strdir, true);
}
}
return true;
}
catch (Exception Exp)
{
System.Diagnostics.Debug.Write(Exp.Message);
return false;
}
}
#endregion
public static double GetFileSize(HttpPostedFileBase files)
{
return GetFileSize(files.ContentLength);
}
/// <summary>
/// 格式化文件大小
/// </summary>
/// <param name="filesize">文件传入大小</param>
/// <returns></returns>
private static double GetFileSize(long filesize)
{
try
{
if (filesize < 0)
{
return 0;
}
else {
return (double)filesize / (1024 * 1024);
}
/*else if (filesize >= 1024 * 1024 * 1024) //文件大小大于或等于1024MB
{
return string.Format("{0:0.00} GB", (double)filesize / (1024 * 1024 * 1024));
}
else if (filesize >= 1024 * 1024) //文件大小大于或等于1024KB
{
return string.Format("{0:0.00} MB", (double)filesize / (1024 * 1024));
}
else if (filesize >= 1024) //文件大小大于等于1024bytes
{
return string.Format("{0:0.00} KB", (double)filesize / 1024);
}
else
{
return string.Format("{0:0.00} bytes", filesize);
}*/
}
catch (Exception ex)
{
throw ex;
}
}
}
}
代码到这里我就不得不说我踩到的一个大坑,当时做的时候我解析apk和上传七牛云之间出现的一个bug,我花了两三天去解决这个问题,一开始我以为是代码问题然后去一步步调试,把这两个操作分开都没问题,但是放到一起就出问题了,把解析apk代码放到上传前面,七牛云就抱406,不可接受,第二次我把上传文件代码放到解析apk前面,然后解析apk代码就报读取不了这个压缩文件,就是这个apk文件。我也问了很多大佬,一些交流群里的大佬始终得不到正确的解决方法,后面我也去找了七牛云的技术支持,说406是七牛云的Crc32验证不通过,当时人都蒙了,C#里上传文件没有这个·验证Crc32验证,后面我翻七牛云的SDK文档,发现了一种通过字节流的上传操作,
后面换了这种上传方式居然又行了,还是我太年轻了,注我以前七牛云上传操作都是通过文件流的形式上传
我给你们上文件流上传代码和字节流上传代码
using Qiniu.Http;
using Qiniu.Storage;
using Qiniu.Util;
using System;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using System.Web;
namespace GenernalMVC.Common.Upload
{
/*==================================================================
* 类名 : 上传图片工具类
* 功能 : 上传图片到七牛云
* 作者 :
* 日期 : 20220706
* 修改记录:
/*==================================================================*/
public class UploadImage
{
string AccessKey = ConfigurationManager.AppSettings["AccessKey"].ToString();
string SecretKey = ConfigurationManager.AppSettings["SecretKey"].ToString();
string QiniuAddress = ConfigurationManager.AppSettings["QiniuAddress"].ToString();
string QiniuAppAddress= ConfigurationManager.AppSettings["QiniuAppAddress"].ToString();
string bucket = "xdjofficial";//这个是我们在七牛云上创建的“存储空间”名称
/// <summary>
/// 通过文件流上传的七牛云,不存本地
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<string> Upload(HttpPostedFileBase file)
{
var newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_fffff", DateTimeFormatInfo.InvariantInfo) + ".jpg";
string saveKey = "xdjmvc/" + newFileName;
//string saveKey = "xdjmvc/" + file.FileName; //这个myfile 相当于一个文件夹,是我们只设定的。2.png是我们上传文件的文件名,上传成功后通过它来读
Mac mac = new Mac(AccessKey, SecretKey); // 生成(上传)凭证时需要使用此Mac
PutPolicy putPolicy = new PutPolicy();
// 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
// putPolicy.Scope = bucket + ":" + saveKey;
putPolicy.Scope = bucket;
// 上传策略有效期(对应于生成的凭证的有效期)
putPolicy.SetExpires(3600);
// 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
//putPolicy.DeleteAfterDays = 1;
putPolicy.ReturnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsiz\":$(fsize),\"bucket\":\"$(bucket)\",\"name\":\"$(x:name)\"}";
// 生成上传凭证
string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString());//token生成
FormUploader target = new FormUploader(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
HttpResult result = await target.UploadStream(file.InputStream, saveKey, token,new PutExtra());
QiniuModel model = new QiniuModel();
model = result.Text.ToObject<QiniuModel>();
if (result.Code==200)
{
return QiniuAddress + "/" + model.key;
}
return null;
}
/// <summary>
/// 删除七牛云里的图片
/// </summary>
/// <param name="filePath">这里是七牛云存的地址</param>
/// <returns></returns>
public async Task<int> Delete(string filePath)
{
string[] sArray = filePath.Split(new string[] { QiniuAddress+"/" }, StringSplitOptions.RemoveEmptyEntries);
BucketManager bucketManager = new BucketManager(new Mac(AccessKey, SecretKey), new Config());
HttpResult result = await bucketManager.Delete(bucket, sArray[0]);
return result.Code;
}
/// <summary>
/// 通过本地地址上传七牛云,存本地
/// </summary>
/// <param name="url">图片本地地址</param>
/// <returns></returns>
public async Task<string> Upload(string url)
{
string saveKey = System.IO.Path.GetFileName(url);
Mac mac = new Mac(AccessKey, SecretKey);
string localFile = url;
PutPolicy putPolicy = new PutPolicy();
// 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
// putPolicy.Scope = bucket + ":" + saveKey;
putPolicy.Scope = bucket;
// 上传策略有效期(对应于生成的凭证的有效期)
putPolicy.SetExpires(3600);
// 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
//putPolicy.DeleteAfterDays = 1;
putPolicy.ReturnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsiz\":$(fsize),\"bucket\":\"$(bucket)\",\"name\":\"$(x:name)\"}";
// 生成上传凭证
string jstr = putPolicy.ToJsonString();
string token = Auth.CreateUploadToken(mac, jstr);
//Config配置类中可以配置区域,比如在我在七牛云上创建的fanin这个存储空间的区域选择的是“华南”这里就将它设为Zone=Zone.ZONE_CN_South
//UseHttps表示:是否采用https域名
//UseCdnDomains表示:上传是否使用CDN上传加速
//MaxRetryTimes表示:重试请求次数
UploadManager um = new UploadManager(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
HttpResult result =await um.UploadFile(localFile, saveKey, token, new PutExtra());
//UploadManager um = new UploadManager(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
//HttpResult result = await um.UploadFile(localFile, saveKey, token, new PutExtra());
QiniuModel model = new QiniuModel();
if (result.Code == 200)
{
model = result.Text.ToObject<QiniuModel>();
}
return QiniuAddress + "/" + model.key;
}
/// <summary>
/// 上传apk安装包
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<string> UploadFile(HttpPostedFileBase file,string url)
{
var aa= Crc32.CheckSumStream(file.InputStream);
string saveKey = "xdjmvc/" + System.IO.Path.GetFileName(url);
//string saveKey = "xdjmvc/" + file.FileName; //这个myfile 相当于一个文件夹,是我们只设定的。2.png是我们上传文件的文件名,上传成功后通过它来读
Mac mac = new Mac(AccessKey, SecretKey); // 生成(上传)凭证时需要使用此Mac
PutPolicy putPolicy = new PutPolicy();
// 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
// putPolicy.Scope = bucket + ":" + saveKey;
putPolicy.Scope = "xdjapk";
// 上传策略有效期(对应于生成的凭证的有效期)
putPolicy.SetExpires(3600);
// 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
//putPolicy.DeleteAfterDays = 1;
putPolicy.ReturnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsiz\":$(fsize),\"bucket\":\"$(bucket)\",\"name\":\"$(x:name)\"}";
// 生成上传凭证
string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString());//token生成
//UploadManager um = new UploadManager(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
//HttpResult result = await um.UploadFile(url, saveKey, token, new PutExtra());
FormUploader target = new FormUploader(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
HttpResult result = await target.UploadStream(file.InputStream, saveKey, token, new PutExtra());
QiniuModel model = new QiniuModel();
model = result.Text.ToObject<QiniuModel>();
if (result.Code == 200)
{
return QiniuAppAddress + "/" + model.key;
}
return null;
}
#region 生成随机字符
/// <summary>
/// 生成随机字符
/// </summary>
/// <param name="length">字符长度</param>
/// <param name="isSleep">是否要在生成前将当前线程阻止以避免重复</param>
/// <param name="type">字符类型"num":数字类型;"str":纯字母;为空或者其他时,是数字和字母混合随机数</param>
/// <returns>随机字符组成的字符串</returns>
public static string Chars(int length, bool isSleep, string type)
{
if (isSleep) System.Threading.Thread.Sleep(3);
char[] chars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
string result = "";
// 给一个时间种子,防止重复
Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
if (type == "num")
{
// 生成纯数字随机数
for (int i = 0; i < length; i++)
{
int rnd = random.Next(10);
result += chars[rnd];
}
}
else if (type == "str")
{
// 纯字母随机数
int rnd = random.Next(11, 36);
result += chars[rnd];
}
else
{
// 数字字符混合随机数
for (int i = 0; i < length; i++)
{
int rnd = random.Next(36);
result += chars[rnd];
}
}
return result;
}
#endregion
/// <summary>
/// 简单上传-上传字节数据
/// </summary>
public async Task<string> UploadData(string url)
{
// 生成(上传)凭证时需要使用此Mac
// 这个示例单独使用了一个Settings类,其中包含AccessKey和SecretKey
// 实际应用中,请自行设置您的AccessKey和SecretKey
Mac mac = new Mac(AccessKey, SecretKey); // 生成(上传)凭证时需要使用此Mac
string bucket = "xdjapk";
string saveKey = "xdjmvc/"+ Chars(6,true,"") + ".apk";
byte[] data = System.IO.File.ReadAllBytes(url);
//byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello World!");
// 上传策略,参见
// https://developer.qiniu.com/kodo/manual/put-policy
PutPolicy putPolicy = new PutPolicy();
// 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
// putPolicy.Scope = bucket + ":" + saveKey;
putPolicy.Scope = bucket;
// 上传策略有效期(对应于生成的凭证的有效期)
putPolicy.SetExpires(3600);
// 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
putPolicy.DeleteAfterDays = 1;
// 生成上传凭证,参见
// https://developer.qiniu.com/kodo/manual/upload-token
string jstr = putPolicy.ToJsonString();
string token = Auth.CreateUploadToken(mac, jstr);
FormUploader fu = new FormUploader(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
Qiniu.Http.HttpResult result =await fu.UploadData(data, saveKey, token, new PutExtra());
QiniuModel model = new QiniuModel();
model = result.Text.ToObject<QiniuModel>();
if (result.Code == 200)
{
return QiniuAppAddress + "/" + model.key;
}
return null;
}
/// <summary>
/// 上传apk安装包
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<string> UploadFile(HttpPostedFileBase file)
{
string name = Chars(6, true, "");
var newFileName = name + ".apk";
string saveKey = "xdjmvc/" + newFileName;
//string saveKey = "xdjmvc/" + file.FileName; //这个myfile 相当于一个文件夹,是我们只设定的。2.png是我们上传文件的文件名,上传成功后通过它来读
Mac mac = new Mac(AccessKey, SecretKey); // 生成(上传)凭证时需要使用此Mac
PutPolicy putPolicy = new PutPolicy();
// 如果需要设置为"覆盖"上传(如果云端已有同名文件则覆盖),请使用 SCOPE = "BUCKET:KEY"
// putPolicy.Scope = bucket + ":" + saveKey;
putPolicy.Scope = "xdjapk";
// 上传策略有效期(对应于生成的凭证的有效期)
putPolicy.SetExpires(3600);
// 上传到云端多少天后自动删除该文件,如果不设置(即保持默认默认)则不删除
//putPolicy.DeleteAfterDays = 1;
putPolicy.ReturnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsiz\":$(fsize),\"bucket\":\"$(bucket)\",\"name\":\"$(x:name)\"}";
// 生成上传凭证
string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString());//token生成
FormUploader target = new FormUploader(new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
BucketManager bucketManager = new BucketManager(mac, new Config() { Zone = Zone.ZoneCnSouth, UseHttps = true, UseCdnDomains = true, MaxRetryTimes = 3 });
await bucketManager.Delete(bucket, saveKey);
HttpResult result = await target.UploadStream(file.InputStream, saveKey, token, new PutExtra());
QiniuModel model = new QiniuModel();
model = result.Text.ToObject<QiniuModel>();
if (result.Code == 200)
{
return QiniuAppAddress + "/" + model.key;
}
return null;
}
}
}
这次这个经历真的好好教育了我,作为年轻人一定得多学习。
对了在补充点小知识,关于前端上传文件到服务器,文件过大解决方法
在webconfig文件里去配置一下代码
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" appRequestQueueLimit="100" executionTimeout="80" maxRequestLength="2147483647" useFullyQualifiedRedirectUrl="false" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483647" />
</requestFiltering>
</security>
</system.webServer>
多了这里我这个类库也是从CSDN上下的,不过都要收费,我这里免费提供给大家,希望大家不喜勿喷,喜欢的点点小赞,支持一下博主,谢谢大家