什么是GeoIP ?

所谓GeoIP,就是通过来访者的IP, 定位他的经纬度,国家/地区,省市,甚至街道等位置信息的一个数据库。GeoIP有两个版本,一个免费版,一个收费版本。收费版本的准确率和数据更好一些。

GeoIP如何使用?

GeoIP支持多种语言调用,这里我们以java为例。

执行下面的命令:

wget -N -q http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz

gzip -d GeoIP.dat.gz

cp GeoIP.dat   /usr/local/share/GeoIP/GeoIP.dat

具体执行代码:

package com.test.intl.commons.ip;

/**

* 这个类封装了对国家的数据。 类Country.java的实现描述:TODO 类实现描述

*

*/

public class Country {

private String code;

private String name;

/**

* Creates a new Country.

*

* @param code the country code.

* @param name the country name.

*/

public Country(String code, String name) {

this.code = code;

this.name = name;

}

/**

* Returns the ISO two-letter country code of this country.

*

* @return the country code.

*/

public String getCode() {

return code;

}

/**

* Returns the name of this country.

*

* @return the country name.

*/

public String getName() {

return name;

}

}

package com.test.intl.commons.ip;

import java.io.File;

import java.io.IOException;

import java.net.InetAddress;

import java.util.HashMap;

import org.apache.log4j.Logger;

import sun.net.util.IPAddressUtil;

import com.maxmind.geoip.DatabaseInfo;

public class CountryLookupService {

/**

* 这个是数据文件的。

*/

private File                                  databaseFile              = null;

/**

* buffer最大的数据。

*/

private int                                   maxBuffer                 = 0;

/**

* 判断是否进行了初始化工作。

*/

private boolean                               isInit                    = false;

//

private static Logger                         log                       = Logger.getLogger(CountryLookupService.class);

/**

* 每次进行check的时间间隔,默认是1分钟一次。

*/

private int                                   checkfreshInvertal        = 60 * 1000;

/**

* 最后一次做check的时间间隔。

*/

private long                                  lastcheckfreshTime        = 0;

/**

* Database file.

*/

private SafeBufferRandomAccessFile            file                      = null;

/**

* Information about the database.

*/

// private DatabaseInfo databaseInfo = null;

/**

* The database type. Default is the country edition.

*/

byte                                          databaseType              = DatabaseInfo.COUNTRY_EDITION;

int                                           databaseSegments[];

int                                           recordLength;

String                                        licenseKey;

int                                           dnsService                = 0;

private final static int                      COUNTRY_BEGIN             = 16776960;

private final static int                      STATE_BEGIN               = 16700000;

private final static int                      STRUCTURE_INFO_MAX_SIZE   = 20;

// private final static int DATABASE_INFO_MAX_SIZE = 100;

private final static int                      SEGMENT_RECORD_LENGTH     = 3;

private final static int                      STANDARD_RECORD_LENGTH    = 3;

private final static int                      ORG_RECORD_LENGTH         = 4;

private final static int                      MAX_RECORD_LENGTH         = 4;

// private final static int MAX_ORG_RECORD_LENGTH = 300;

// private final static int FULL_RECORD_LENGTH = 50;

public static final Country                   UNKNOWN_COUNTRY           = new Country("--", "N/A");

private final static HashMap hashmapcountryCodetoindex = new HashMap(512);

private final static HashMap hashmapcountryNametoindex = new HashMap(512);

private final static String[]                 countryCode               = { "--", "AP", "EU", "AD", "AE", "AF",

"AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF",

"BG", "BH", "BI", "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF",

"CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM",

"DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB",

"GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM",

"HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG",

"KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU",

"LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU",

"MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM",

"PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RU",

"RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV",

"SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TM", "TN", "TO", "TP", "TR", "TT", "TV", "TW", "TZ",

"UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "YU",

"ZA", "ZM", "ZR", "ZW", "A1", "A2", "O1"                       };

private final static String[]                 countryName               = { "N/A", "Asia/Pacific Region", "Europe",

"Andorra", "United Arab Emirates", "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia",

"Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", "Austria", "Australia",

"Aruba", "Azerbaijan", "Bosnia and Herzegovina", "Barbados", "Bangladesh", "Belgium", "Burkina Faso",

"Bulgaria", "Bahrain", "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", "Bahamas",

"Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", "Canada", "Cocos (Keeling) Islands",

"Congo, The Democratic Republic of the", "Central African Republic", "Congo", "Switzerland",

"Cote D'Ivoire", "Cook Islands", "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba",

"Cape Verde", "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", "Denmark", "Dominica",

"Dominican Republic", "Algeria", "Ecuador", "Estonia", "Egypt", "Western Sahara", "Eritrea", "Spain",

"Ethiopia", "Finland", "Fiji", "Falkland Islands (Malvinas)", "Micronesia, Federated States of",

"Faroe Islands", "France", "France, Metropolitan", "Gabon", "United Kingdom", "Grenada", "Georgia",

"French Guiana", "Ghana", "Gibraltar", "Greenland", "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea",

"Greece", "South Georgia and the South Sandwich Islands", "Guatemala", "Guam", "Guinea-Bissau", "Guyana",

"Hong Kong", "Heard Island and McDonald Islands", "Honduras", "Croatia", "Haiti", "Hungary", "Indonesia",

"Ireland", "Israel", "India", "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of",

"Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", "Cambodia", "Kiribati", "Comoros",

"Saint Kitts and Nevis", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait",

"Cayman Islands", "Kazakstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia",

"Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg", "Latvia",

"Libyan Arab Jamahiriya", "Morocco", "Monaco", "Moldova, Republic of", "Madagascar", "Marshall Islands",

"Macedonia, the Former Yugoslav Republic of", "Mali", "Myanmar", "Mongolia", "Macau",

"Northern Mariana Islands", "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives",

"Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", "Niger", "Norfolk Island",

"Nigeria", "Nicaragua", "Netherlands", "Norway", "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama",

"Peru", "French Polynesia", "Papua New Guinea", "Philippines", "Pakistan", "Poland",

"Saint Pierre and Miquelon", "Pitcairn", "Puerto Rico", "" + "Palestinian Territory, Occupied", "Portugal",

"Palau", "Paraguay", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saudi Arabia",

"Solomon Islands", "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", "Slovenia",

"Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", "Somalia", "Suriname",

"Sao Tome and Principe", "El Salvador", "Syrian Arab Republic", "Swaziland", "Turks and Caicos Islands",

"Chad", "French Southern Territories", "Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan",

"Tunisia", "Tonga", "East Timor", "Turkey", "Trinidad and Tobago", "Tuvalu", "Taiwan",

"Tanzania, United Republic of", "Ukraine", "Uganda", "United States Minor Outlying Islands",

"United States", "Uruguay", "Uzbekistan", "Holy See (Vatican City State)",

"Saint Vincent and the Grenadines", "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.",

"Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", "Yugoslavia", "South Africa",

"Zambia", "Zaire", "Zimbabwe", "Anonymous Proxy", "Satellite Provider", "Other" };

/**

* Create a new lookup service using the specified database file.

*

* @param databaseFile String representation of the database file.

* @throws java.io.IOException if an error occured creating the lookup service from the database file.

*/

public CountryLookupService(String databaseFile, int maxBuffer) {

this(new File(databaseFile), maxBuffer);

}

/**

* Create a new lookup service using the specified database file.

*

* @param databaseFile the database file.

* @throws java.io.IOException if an error occured creating the lookup service from the database file.

*/

public CountryLookupService(File databaseFile, int maxBuffer) {

lastcheckfreshTime = System.currentTimeMillis();

this.databaseFile = databaseFile;

this.maxBuffer = maxBuffer;

// 这里可能出现文件不存在的情况,这样的情况,主要考虑到一些应用不会使用到这个东西,所以我们认为是合理的。

try {

initDataFile();

} catch (Exception ex) {

log.error(ex);

}

}

private void initDataFile() throws IOException {

this.file = new SafeBufferRandomAccessFile(this.databaseFile, this.maxBuffer);

this.file.start();

init();

this.file.end();

isInit = true;

}

/**

* 这个值,每隔1分钟做一次check,如果该数据没有做初始化的工作 那么在check满足一份钟的前提下,我们会做一次初始化的工作。 进行初始化的工作,我们认为是正确的。

*

* @return

*/

public boolean checkNeedfresh() {

if (System.currentTimeMillis() - lastcheckfreshTime > checkfreshInvertal) {

lastcheckfreshTime = System.currentTimeMillis();

// 如果没有做初始化工作,那么要先做初始化的工作,然后在做处理。

if (!isInit || this.file == null) {

if (this.databaseFile.exists()) {

synchronized (this) {

if (!isInit || this.file == null) {

try {

initDataFile();

} catch (Exception e) {

log.error(e);

}

}

}

}

return false;

}

return this.file.needfresh();

} else {

return false;

}

}

/**

* Reads meta-data from the database file.

*

* @throws java.io.IOException if an error occurs reading from the database file.

*/

private void init() throws IOException {

int i, j;

byte[] delim = new byte[3];

byte[] buf = new byte[SEGMENT_RECORD_LENGTH];

for (i = 0; i < 233; i++) {

hashmapcountryCodetoindex.put(countryCode[i], new Integer(i));

hashmapcountryNametoindex.put(countryName[i], new Integer(i));

}

if (file == null) {

// distributed service only

return;

}

file.seek(file.length() - 3);

for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {

file.read(delim);

if (delim[0] == -1 && delim[1] == -1 && delim[2] == -1) {

databaseType = file.readByte();

if (databaseType >= 106) {

// Backward compatibility with databases from April 2003 and earlier

databaseType -= 105;

}

// Determine the database type.

if (databaseType == DatabaseInfo.REGION_EDITION) {

databaseSegments = new int[1];

databaseSegments[0] = STATE_BEGIN;

recordLength = STANDARD_RECORD_LENGTH;

} else if (databaseType == DatabaseInfo.CITY_EDITION_REV0

|| databaseType == DatabaseInfo.CITY_EDITION_REV1

|| databaseType == DatabaseInfo.ORG_EDITION || databaseType == DatabaseInfo.ISP_EDITION) {

databaseSegments = new int[1];

databaseSegments[0] = 0;

if (databaseType == DatabaseInfo.CITY_EDITION_REV0

|| databaseType == DatabaseInfo.CITY_EDITION_REV1) {

recordLength = STANDARD_RECORD_LENGTH;

} else {

recordLength = ORG_RECORD_LENGTH;

}

file.read(buf);

for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) {

databaseSegments[0] += (unsignedByteToInt(buf[j]) << (j * 8));

}

}

break;

} else {

file.seek(file.getFilePointer() - 4);

}

}

if (databaseType == DatabaseInfo.COUNTRY_EDITION) {

databaseSegments = new int[1];

databaseSegments[0] = COUNTRY_BEGIN;

recordLength = STANDARD_RECORD_LENGTH;

}

}

/**

* Closes the lookup service.

*/

public void close() {

try {

file.close();

file = null;

} catch (Exception e) {

log.error(e);

}

}

/**

* Returns the country the IP address is in.

*

* @param ipAddress String version of an IP address, i.e. "127.0.0.1"

* @return the country the IP address is from.

*/

public Country getCountry(String ipAddress) {

// 如果不能正常的初始化,那么我们不会出错,但是返回一个未识别的国家

if (this.file == null) {

return UNKNOWN_COUNTRY;

}

if (IPAddressUtil.isIPv4LiteralAddress(ipAddress) || IPAddressUtil.isIPv6LiteralAddress(ipAddress)) {

try {

return getCountry(bytesToLong(InetAddress.getByName(ipAddress).getAddress()));

} catch (Throwable e) {

log.error(e);

}

}

//

return UNKNOWN_COUNTRY;

}

/**

* Returns the country the IP address is in.

*

* @param ipAddress the IP address.

* @return the country the IP address is from.

*/

public Country getCountry(InetAddress ipAddress) {

return getCountry(bytesToLong(ipAddress.getAddress()));

}

/**

* Returns the country the IP address is in.

*

* @param ipAddress the IP address in long format.

* @return the country the IP address is from.

*/

public Country getCountry(long ipAddress) {

if (file == null) {

throw new IllegalStateException("Database has been closed.");

}

int ret = warpSeekCountry(ipAddress) - COUNTRY_BEGIN;

if (ret == 0) {

return UNKNOWN_COUNTRY;

} else {

return new Country(countryCode[ret], countryName[ret]);

}

}

protected int warpSeekCountry(long ipAddress) {

this.file.start();

try {

return this.seekCountry(ipAddress);

} finally {

this.file.end();

}

}

/**

* Finds the country index value given an IP address.

*

* @param ipAddress the ip address to find in long format.

* @return the country index.

*/

private int seekCountry(long ipAddress) {

byte[] buf = new byte[2 * MAX_RECORD_LENGTH];

int[] x = new int[2];

int offset = 0;

for (int depth = 31; depth >= 0; depth--) {

try {

file.seek(2 * recordLength * offset);

file.read(buf);

} catch (IOException e) {

log.error(e);

}

for (int i = 0; i < 2; i++) {

x[i] = 0;

for (int j = 0; j < recordLength; j++) {

int y = buf[i * recordLength + j];

if (y < 0) {

y += 256;

}

x[i] += (y << (j * 8));

}

}

if ((ipAddress & (1 << depth)) > 0) {

if (x[1] >= databaseSegments[0]) {

return x[1];

}

offset = x[1];

} else {

if (x[0] >= databaseSegments[0]) {

return x[0];

}

offset = x[0];

}

}

// shouldn't reach here

log.error("Error seeking country while seeking " + ipAddress);

return 0;

}

/**

* Returns the long version of an IP address given an InetAddress object.

*

* @param address the InetAddress.

* @return the long form of the IP address.

*/

private static long bytesToLong(byte[] address) {

long ipnum = 0;

for (int i = 0; i < 4; ++i) {

long y = address[i];

if (y < 0) {

y += 256;

}

ipnum += y << ((3 - i) * 8);

}

return ipnum;

}

private static int unsignedByteToInt(byte b) {

return (int) b & 0xFF;

}

}

package com.test.intl.commons.ip;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.RandomAccessFile;

/**

* 这个是用于实现线程安全的结构。 类SafeBufferRandomAccessFile.java的实现描述:TODO 类实现描述

*

*/

public class SafeBufferRandomAccessFile {

private File                      f                = null;

private RandomAccessFile          file;

/**

* buffer可以支持的最大的长度,超过这个长度就报错误。

*/

private int                       maxSize          = 0;

/**

* 缓存的数据,这个只有一次的机会

*/

private byte[]                    buffer;

/**

* 用于缓存当前线程的状态,这个做法虽然慢一点,但是可以不破坏原来的功能,而且检索锁竞争,应该足够了。

*/

private static ThreadLocal pointLocal       = new ThreadLocal();

/**

* 文件的长度,这个一定是int的,这个不是为了大文件服务的。

*/

private int                       fileSize         = 0;

private long                      lastmodifiedTime = 0;

public SafeBufferRandomAccessFile(File f, int maxSize) throws IOException, FileNotFoundException {

this.f = f;

file = new RandomAccessFile(f, "r");

this.maxSize = maxSize;

readToBuffer();

lastmodifiedTime = f.lastModified();

}

public void readToBuffer() throws IOException {

long size;

if ((size = file.length()) > maxSize) {

throw new SaftBufferOverflowException("file path = [" + f.getAbsolutePath() + "] length=[" + size + "]");

}

buffer = new byte[(int) size];

file.read(buffer, 0, buffer.length);

fileSize = buffer.length;

}

/**

* 开始一系列的操作。 启动后,所有的操作都是线程安全的。 每次启动的时候,会重置所有的内容。

*/

public void start() {

pointLocal.set(new Point(1));

}

/**

* 关闭一次请求, 关闭后,所有的操作就不再是线程安全的。

*/

public void end() {

pointLocal.set(null);

}

/**

* 表示该文件是否需要刷新

*

* @return

*/

public boolean needfresh() {

if (lastmodifiedTime == f.lastModified()) {

return false;

}

return true;

}

/**

* 关闭当前文件并进行释放所有的资源。

*

* @throws IOException

*/

public void close() throws IOException {

this.buffer = null;

this.file.close();

}

public long getFilePointer() throws IOException {

return pointLocal.get().curPos;

}

public long length() throws IOException {

return this.fileSize;

}

private int innerLength() {

return this.fileSize;

}

public int read() throws IOException {

byte[] b = new byte[1];

int i = read(b, 0, b.length);

if (i == -1) {

return -1;

}

return (int) b[0];

}

// public boolean checkNeedfresh(){

//

// }

public int read(byte[] b, int off, int len) throws IOException {

// 由于b是从0开始的,所以这里的偏移量要小一位。所以这里需要等于号。

if (off + len > b.length) {

return 0;

}

int cur = pointLocal.get().curPos;

if (cur == -1) return -1;

int readSize = 0;

// 如果剩余的字段比len要长的话,那么直接读取len长度的内容。

if (innerLength() - cur >= len) {

System.arraycopy(buffer, cur, b, off, len);

cur = cur + len;

readSize = len;

// 超出末尾了。

} else {

System.arraycopy(buffer, cur, b, off, innerLength() - cur);

cur = -1;

readSize = -1;

}

pointLocal.get().curPos = cur;

return readSize;

}

public int read(byte[] b) throws IOException {

return read(b, 0, b.length);

}

/**

* 指的是从文件起始位置跳过制定的pos的位置。 pos小于0,什么都不做。 当seek不会超出,只会设置到最末尾。 这个语意stream读取是一致的,但是写入的不一致,所以也是正确的。

*

* @param pos

* @throws IOException

*/

public void seek(long pos) throws IOException {

if (pos < 0) return;

pointLocal.get().curPos = (int) (pos > length() ? length() : pos);

}

/**

* 跳过指定n的数量的byte,并丢弃。

*

* @param n

* @return 实际跳过的byte数量。

* @throws IOException

*/

public int skipBytes(int n) throws IOException {

if (n <= 0) {

return 0;

}

long pos = getFilePointer();

long len = length();

long newpos = pos + n;

if (newpos > len) {

newpos = len;

}

seek(newpos);

return (int) (newpos - pos);

}

public byte readByte() throws IOException {

return (byte) this.read();

}

private class Point {

public Point(int curPos) {

this.curPos = curPos;

}

private int curPos;

}

}

package com.test.intl.commons.ip;

/**

* buffer的内容溢出的异常。 类SaftBufferOverflowException.java的实现描述:TODO 类实现描述

*

*/

public class SaftBufferOverflowException extends RuntimeException {

private static final long serialVersionUID = 6433209191770907106L;

public SaftBufferOverflowException() {

super();

}

public SaftBufferOverflowException(String message) {

super(message);

}

public SaftBufferOverflowException(String message, Throwable cause) {

super(message, cause);

}

public SaftBufferOverflowException(Throwable cause) {

super(cause);

}

}

测试代码:

package com.test.intl.commons.ip;

public class TestCountry {

private static final String geoidFile  = "/usr/local/share/GeoIP/GeoIP.dat";

/** max buffer size for the GeoIP file, 3M */

private static final int    MAX_BUFFER = 1024 * 1024 * 3;

public static void main(String[] args) {

Country country = new CountryLookupService(geoidFile, MAX_BUFFER).getCountry("2001:0db8:85a3:08d3:1319:8a2e:0370:7344");

System.out.println(country.getCode() + ";" + country.getName());

}

}

java ip 国家_java通过ip获取用户所在国家 | 学步园相关推荐

  1. java biginteger使用_java中的BigInteger的基本用法 | 学步园

    注 : 为了更好的理解BigInteger类,特提供了一篇关于BigInteger的题目及本人的解答,感兴趣的朋友可以到我的博客看一下 import java.math.BigInteger; pub ...

  2. java dll 乱码_java调用c++ dll出现中文乱码 | 学步园

    最近的开发用到了使用java调用本机动态连接库的功能,将文件路径通过java调用C++代码对文件进行操作.在调用中如果路径中包含有中文字符就会出现问题,程序运行就会中止.下面用一个小例子,来说明记录下 ...

  3. java 栈 泛型_java 泛型栈(数组实现) | 学步园

    尝试将一些数据结构用java实现,尝试过程中确实碰到一些问题,收获很大import java.lang.reflect.Array; class ArrayStack { Class type; pr ...

  4. java web定时器_java的web项目中使用定时器 | 学步园

    之前接触过程序中的定时任务,但是没去自己亲自尝试过.终于这次抽空搞了一下.(一定要自己去操作,才能长经验,光看到过是没用的) 以下是两种方法,我使用的是监听的方法. JAVA WEB定时器,定时器的启 ...

  5. 根据ip或者自动识别当前用户所在国家、地区、城市

    今天给大家来点干货! 完整需求:根据用户所在地的不同,网站自动切换最适合用户的语言 解析需求: 1.我们需要根据ip获取用户所在地,其中获取IP具体有三种方法: (1).在项目中引入搜狐这个组件库 然 ...

  6. 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...

  7. 微信小程序结合腾讯位置服务获取用户所在城市信息

    背景 实现小程序进去后获取用户当前所在城市,然后显示该城市的数据,并且显示在导航栏和 Tab上. 微信小程序中,我们可以通过调用wx.getLocation()获取到设备当前的地理位置信息,这个信息是 ...

  8. 【微信小程序腾讯位置服务】获取用户所在城市信息实战

    背景 实现小程序进去后获取用户当前所在城市,然后显示该城市的数据,并且显示在导航栏和 Tab上. 微信小程序中,我们可以通过调用wx.getLocation()获取到设备当前的地理位置信息,这个信息是 ...

  9. js html5 省份 城市,如何使用JS获取用户所在城市及地理位置

    这次给大家带来如何使用JS获取用户所在城市及地理位置,如何使用JS获取用户所在城市及地理位置的注意事项有哪些,下面就是实战案例,一起来看一下. 获取用户地理位置 $.getScript('http:/ ...

最新文章

  1. IssueVission的命令处理
  2. 让系统命令man汉化
  3. Android studio下将项目代码上传至github包括更新,同步,创建依赖
  4. Spring配置文件applicationContext.xml的两种拆分方式
  5. 《剑指offer》数组中出现次数超过一半的数字
  6. Android为TV端助力 转载:android自定义view实战(温度控制表)!
  7. P91--商品保存debug完成
  8. Java多线程学习笔记-线程的使用
  9. 关于 XHTML 1.0 Transitional//EN 下的Relative问题
  10. 北京十大著名拆迁律师事务所(精品拆迁律师)
  11. 陆地生态系统生态学原理
  12. 大学一年级计算机应用课程,计算机应用*的课程教学计划
  13. ResNet之残差结构的理解
  14. gp库创建分区表方法
  15. egg.js 获取客户端真实ip
  16. week1-Python学习总结
  17. std::distance 用法
  18. 热力学相关的两个定律
  19. unity使用Dijkstra算法实现自动寻路
  20. Curvature与Radius of curvature的区别

热门文章

  1. Kotlin 非对称加密RSA
  2. JAVA基础-06.集合-14.【List、Set】
  3. android按键模拟测试
  4. 一位老学长的真实互联网校招求职心路历程~
  5. 「解决方案」Acrel-2000Z变电站综合自动化系统
  6. 政府数据运营哪家强?浪潮!
  7. 安装win10自带Linux子系统并用win10商店安装win10自带的Ubuntu
  8. 在ie edge中消除默认出现的密码框小眼睛标志
  9. FT-TRN-BEG-C安装教程及问题解决
  10. 《编程之美》 查找最大(小)的k个元素