xutils工具上传日志文件
首先下载xutils java包:
添加到项目的工程中:
第二在新建一个类继承application
package logback.ecmapplication.cetcs.com.myapplication; import android.app.Application; /** * Created by wei.yuan on 2017/9/13. */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xutils.x; public class EcmsApplication extends Application { public static Logger log = null; @Override public void onCreate() { super.onCreate(); x.Ext.init(this); x.Ext.setDebug(true); log = LoggerFactory.getLogger(EcmsApplication.class); } }
在清单文件中配置该类
<application android:name=".EcmsApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
在代码中就可以使用该
进行上传了
功能:把
/****
*
* 几点注意事项:
使用logback框架把app运行的日志存储到
android/data/com.cetcs.ecmapplication/logs目录下
* 1、如果app存在代码混淆,对功能有影响,代码混淆不能混淆logback和xutils
* 2、运行app的时候一定要记得把对应的存储权限打开,在android 6.0以上要做权限检查
* 把手机内部存储android/data/com.cetcs.ecmapplication/logs下的文件压缩成zip文件
* zip文件存储在android/data/com.cetcs.ecmapplication/目录下ecmslog.zip
* 然后使用xutils将android/data/com.cetcs.ecmapplication/ecmslog.zip上传到
* http://10.12.8.8:8080/UpLoad_file/upload后台
* */
package logback.ecmapplication.cetcs.com.myapplication; import android.app.Activity; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xutils.common.Callback; import org.xutils.db.sqlite.KeyValue; import org.xutils.http.RequestParams; import org.xutils.http.body.MultipartBody; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.xutils.x; import static logback.ecmapplication.cetcs.com.myapplication.EcmsApplication.log; /**** * * 几点注意事项: * 1、如果app存在代码混淆,对功能有影响 * 2、运行app的时候一定要记得把对应的存储权限打开,在android 6.0以上要做权限检查 * 把手机内部存储android/data/com.cetcs.ecmapplication/logs下的文件压缩成zip文件 * zip文件存储在android/data/com.cetcs.ecmapplication/目录下ecmslog.zip * 然后使用xutils将android/data/com.cetcs.ecmapplication/ecmslog.zip上传到 * http://10.12.8.8:8080/UpLoad_file/upload后台 * */ public class MainActivity extends Activity { private Button btn; // Log.d(ECM_TAG, "->" + tag + ":" + msg) private final static String CLASS_TAG = "MainActivity"; private final static String ECM_TAG = "ecms2223"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "522552522525"); new Thread(new Runnable() { @Override public void run() { log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "2323222522552522525"); } }).start(); new Thread(new Runnable() { @Override public void run() { log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "232322253533636522552522525"); } }).start(); /* log.info(ECM_TAG,CLASS_TAG,"骂我们skkjfskjjkfsjk"); log.warn(CLASS_TAG,"424424242422424"); log.error(CLASS_TAG,"jkklsakkfa2322323"); log.getName();*/ btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Toast.makeText(MainActivity.this,"上传文件",Toast.LENGTH_LONG).show(); Toast.makeText(MainActivity.this,"上传文件dssd",Toast.LENGTH_LONG).show(); new Thread(new Runnable() { @Override public void run() { ///storage/emulated/0/android/data/com.cetcs.ecmapplication/logs String sourceFilePath = Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"+File.separator+"logs"; String zipFilePath = Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"; log.error("jkklsakkfa2322323 sourceFilePath :"+sourceFilePath); String fileName = "ecmslog"; boolean flag = FileToZip.fileToZip(sourceFilePath, zipFilePath, fileName); if(flag){ log.info(" jkklsakkfa2322323文件打包成功!"); }else{ log.info(" jkklsakkfa2322323文件打包失败"); } final File f = new File(Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"+File.separator+"ecmslog.zip"); log.info(" jkklsakkfa2322323上传文件路径{}"+f.toString()); if(f!= null && f.exists()){ log.info(" jkklsakkfa2322323上传文件存在"); }else{ log.info(" jkklsakkfa2322323上传文件不存在"); return; } RequestParams params = new RequestParams("http://10.12.8.8:8080/UpLoad_file/upload"); params.setMultipart(true); params.addBodyParameter("imei",SystemUtil.getIMEI(getApplication())); params.addBodyParameter("phoneNumber","18780279472"); params.addBodyParameter("phone_model",SystemUtil.getDeviceBrand()+":"+SystemUtil.getSystemModel()); params.addBodyParameter("appPackageName","com.cetcs.ecmapplication"); params.addBodyParameter("app_versionName","1.7.8.1"); params.addBodyParameter("app_versionCode","200"); params.addBodyParameter("File",f,null,"ecmslog.zip"); x.http().post(params, new Callback.CommonCallback<File>() { @Override public void onSuccess(File file) { log.error("jkklsakkfa2322323 onSuccess"); Toast.makeText(MainActivity.this,file.getName()+"文件成功",Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable throwable, boolean b) { log.error("jkklsakkfa2322323 onError"); Toast.makeText(MainActivity.this,f.getName()+"上传文件失败"+throwable.getMessage(),Toast.LENGTH_LONG).show(); } @Override public void onCancelled(CancelledException e) { log.error("jkklsakkfa2322323 onCancelled"); } @Override public void onFinished() { log.error("jkklsakkfa2322323 onFinished"); } }); } }).start(); } }); } }
FileToZip是将
/**
* 将文件夹下面的文件
* 打包成zip压缩文件
*
* @author admin
*
*/如果目录下存储文件夹不全部都是文件就会存在问题
如果目录下的文件名存在中文或者乱码压缩也会存在问题
判断一个字符串是否存在乱码的代码
http://www.jianshu.com/p/77e4aaebd694
package logback.ecmapplication.cetcs.com.myapplication; /** * Created by wei.yuan on 2017/9/13. */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * 将文件夹下面的文件 * 打包成zip压缩文件 * * @author admin * */ public final class FileToZip { private FileToZip(){} /** * 将存放在sourceFilePath目录下的源文件,打包成fileName名称的zip文件,并存放到zipFilePath路径下 * @param sourceFilePath :待压缩的文件路径 * @param zipFilePath :压缩后存放路径 * @param fileName :压缩后文件的名称 * @return */ public static boolean fileToZip(String sourceFilePath,String zipFilePath,String fileName){ boolean flag = false; File sourceFile = new File(sourceFilePath); FileInputStream fis = null; BufferedInputStream bis = null; FileOutputStream fos = null; ZipOutputStream zos = null; if(sourceFile.exists() == false){ System.out.println("待压缩的文件目录:"+sourceFilePath+"不存在."); }else{ try { File zipFile = new File(zipFilePath + "/" + fileName +".zip"); if(zipFile.exists()){ System.out.println(zipFilePath + "目录下存在名字为:" + fileName +".zip" +"打包文件."); }else{ File[] sourceFiles = sourceFile.listFiles(); if(null == sourceFiles || sourceFiles.length<1){ System.out.println("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩."); }else{ fos = new FileOutputStream(zipFile); zos = new ZipOutputStream(new BufferedOutputStream(fos)); byte[] bufs = new byte[1024*10]; for(int i=0;i<sourceFiles.length;i++){ //创建ZIP实体,并添加进压缩包 ZipEntry zipEntry = new ZipEntry(sourceFiles[i].getName()); zos.putNextEntry(zipEntry); //读取待压缩的文件并写进压缩包里 fis = new FileInputStream(sourceFiles[i]); bis = new BufferedInputStream(fis, 1024*10); int read = 0; while((read=bis.read(bufs, 0, 1024*10)) != -1){ zos.write(bufs,0,read); } } flag = true; } } } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } finally{ //关闭流 try { if(null != bis) bis.close(); if(null != zos) zos.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } return flag; } public static void main(String[] args){ String sourceFilePath = "D:\\TestFile"; String zipFilePath = "D:\\tmp"; String fileName = "12700153file"; boolean flag = FileToZip.fileToZip(sourceFilePath, zipFilePath, fileName); if(flag){ System.out.println("文件打包成功!"); }else{ System.out.println("文件打包失败!"); } } }
上面的代码所以需要修改下判断
File[] sourceFiles = sourceFile.listFiles();中
sourceFiles如果是文件夹就continue,不进行压缩
上面的代码中还使用到了logback日志收集系统
logback.xml存放在

<configuration > <property name="LOG_DIR" value="/sdcard/Android/data/com.cetcs.ecmapplication/logs" /> <property name="APP_Name" value="sbapp" /> <contextName>${APP_Name}</contextName> <!-- 全部日志:用于日志输出到独立文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/ecmsLogger(java).log</file> <append>true</append> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %p [%M@%L]-> %m%n</pattern> </encoder> <!-- Rolling Policy --> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>${LOG_DIR}/ecmsLogger(java).%i.log</fileNamePattern> <minIndex>1</minIndex> <maxIndex>5</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <!-- Only log error messages to log file --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> </appender> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>[%d{yyyyMMdd HH:mm:ss-SSS}] %-5level [%thread][%logger:%line]- %msg%n</pattern> </layout> <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> --> </appender> <root level="INFO"> <appender-ref ref="FILE" /> <appender-ref ref="STDOUT" /> </root> </configuration>
logback就是把app的日志保存到
/sdcard/Android/data/com.cetcs.ecmapplication/logs目录下 需要下面的两个jar包

记住代码千万不能对上面的两个java包进行混淆,并且在手机要要保证对应的app的存储权限打开
package logback.ecmapplication.cetcs.com.myapplication; import android.app.Activity; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xutils.common.Callback; import org.xutils.db.sqlite.KeyValue; import org.xutils.http.RequestParams; import org.xutils.http.body.MultipartBody; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.xutils.x; import static logback.ecmapplication.cetcs.com.myapplication.EcmsApplication.log; /**** * * 几点注意事项: * 1、如果app存在代码混淆,对功能有影响 * 2、运行app的时候一定要记得把对应的存储权限打开,在android 6.0以上要做权限检查 * 把手机内部存储android/data/com.cetcs.ecmapplication/logs下的文件压缩成zip文件 * zip文件存储在android/data/com.cetcs.ecmapplication/目录下ecmslog.zip * 然后使用xutils将android/data/com.cetcs.ecmapplication/ecmslog.zip上传到 * http://10.12.8.8:8080/UpLoad_file/upload后台 * */ public class MainActivity extends Activity { private Button btn; // Log.d(ECM_TAG, "->" + tag + ":" + msg) private final static String CLASS_TAG = "MainActivity"; private final static String ECM_TAG = "ecms2223"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "522552522525"); new Thread(new Runnable() { @Override public void run() { log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "2323222522552522525"); } }).start(); new Thread(new Runnable() { @Override public void run() { log.info(ECM_TAG+"->" + CLASS_TAG + ":" + "232322253533636522552522525"); } }).start(); /* log.info(ECM_TAG,CLASS_TAG,"骂我们skkjfskjjkfsjk"); log.warn(CLASS_TAG,"424424242422424"); log.error(CLASS_TAG,"jkklsakkfa2322323"); log.getName();*/ btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Toast.makeText(MainActivity.this,"上传文件",Toast.LENGTH_LONG).show(); Toast.makeText(MainActivity.this,"上传文件dssd",Toast.LENGTH_LONG).show(); new Thread(new Runnable() { @Override public void run() { ///storage/emulated/0/android/data/com.cetcs.ecmapplication/logs String sourceFilePath = Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"+File.separator+"logs"; String zipFilePath = Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"; log.error("jkklsakkfa2322323 sourceFilePath :"+sourceFilePath); String fileName = "ecmslog"; boolean flag = FileToZip.fileToZip(sourceFilePath, zipFilePath, fileName); if(flag){ log.info(" jkklsakkfa2322323文件打包成功!"); }else{ log.info(" jkklsakkfa2322323文件打包失败"); } final File f = new File(Environment.getExternalStorageDirectory()+File.separator+"android"+ File.separator+"data"+File.separator+"com.cetcs.ecmapplication"+File.separator+"ecmslog.zip"); log.info(" jkklsakkfa2322323上传文件路径{}"+f.toString()); if(f!= null && f.exists()){ log.info(" jkklsakkfa2322323上传文件存在"); }else{ log.info(" jkklsakkfa2322323上传文件不存在"); return; } RequestParams params = new RequestParams("http://10.12.8.8:8080/UpLoad_file/upload"); params.setMultipart(true); params.addBodyParameter("imei",SystemUtil.getIMEI(getApplication())); params.addBodyParameter("phoneNumber","18780279472"); params.addBodyParameter("phone_model",SystemUtil.getDeviceBrand()+":"+SystemUtil.getSystemModel()); params.addBodyParameter("appPackageName","com.cetcs.ecmapplication"); params.addBodyParameter("app_versionName","1.7.8.1"); params.addBodyParameter("app_versionCode","200"); params.addBodyParameter("File",f,null,"ecmslog.zip"); x.http().post(params, new Callback.CommonCallback<File>() { @Override public void onSuccess(File file) { log.error("jkklsakkfa2322323 onSuccess"); Toast.makeText(MainActivity.this,file.getName()+"文件成功",Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable throwable, boolean b) { log.error("jkklsakkfa2322323 onError"); Toast.makeText(MainActivity.this,f.getName()+"上传文件失败"+throwable.getMessage(),Toast.LENGTH_LONG).show(); } @Override public void onCancelled(CancelledException e) { log.error("jkklsakkfa2322323 onCancelled"); } @Override public void onFinished() { log.error("jkklsakkfa2322323 onFinished"); } }); } }).start(); } }); } }
整个android项目工程的代码

package logback.ecmapplication.cetcs.com.myapplication; /** * Created by wei.yuan on 2017/9/14. */ import android.app.Activity; import android.content.Context; import android.telephony.TelephonyManager; import java.util.Locale; /** * 系统工具类 * Created by zhuwentao on 2016-07-18. */ public class SystemUtil { /** * 获取当前手机系统语言。 * * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN” */ public static String getSystemLanguage() { return Locale.getDefault().getLanguage(); } /** * 获取当前系统上的语言列表(Locale列表) * * @return 语言列表 */ public static Locale[] getSystemLanguageList() { return Locale.getAvailableLocales(); } /** * 获取当前手机系统版本号 * * @return 系统版本号 */ public static String getSystemVersion() { return android.os.Build.VERSION.RELEASE; } /** * 获取手机型号 * * @return 手机型号 */ public static String getSystemModel() { return android.os.Build.MODEL; } /** * 获取手机厂商 * * @return 手机厂商 */ public static String getDeviceBrand() { return android.os.Build.BRAND; } /** * 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限) * * @return 手机IMEI */ public static String getIMEI(Context ctx) { TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE); if (tm != null) { return tm.getDeviceId(); } return null; } }
在补充一点FileZip文件除了不能压缩目录之外,还不能压缩名字是中文或者乱码的文件,所里还需要对中文和乱码的文件进行判断
package com.cetcs.ecmapplication.dialog; /** * Created by wei.yuan on 2017/9/13. */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.text.DecimalFormat; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * 将文件夹下面的文件 * 打包成zip压缩文件 * * @author admin * */ public final class FileToZip { private FileToZip(){} /** * 注意sourceFilePath目录下的源文件不能有文件夹,只能是文件否则会出现问题 * 将存放在sourceFilePath目录下的源文件,打包成fileName名称的zip文件,并存放到zipFilePath路径下 * @param sourceFilePath :待压缩的文件路径 * @param zipFilePath :压缩后存放路径 * @param fileName :压缩后文件的名称 * @return */ public static boolean fileToZip(String sourceFilePath, String zipFilePath, String fileName){ boolean flag = false; File sourceFile = new File(sourceFilePath); FileInputStream fis = null; BufferedInputStream bis = null; FileOutputStream fos = null; ZipOutputStream zos = null; if(sourceFile.exists() == false){ System.out.println("待压缩的文件目录:"+sourceFilePath+"不存在."); }else{ try { File zipFile = new File(zipFilePath + "/" + fileName +".zip"); if(zipFile.exists()){ System.out.println(zipFilePath + "目录下存在名字为:" + fileName +".zip" +"打包文件."); }else{ File[] sourceFiles = sourceFile.listFiles(); if(null == sourceFiles || sourceFiles.length<1){ System.out.println("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩."); }else{ fos = new FileOutputStream(zipFile); zos = new ZipOutputStream(new BufferedOutputStream(fos)); byte[] bufs = new byte[1024*10]; for(int i=0;i<sourceFiles.length;i++){ //创建ZIP实体,并添加进压缩包 //判断如果是文件夹就不压缩 File f = sourceFiles[i]; if(f.isDirectory()){ continue; } /** * 如果是文件名包含中文,不做压缩,否则会出现问题 * Android�ҕʵ̽˷.pdf * */ if(isMessyCode(f.getName())){ continue; } ZipEntry zipEntry = new ZipEntry(sourceFiles[i].getName()); zos.putNextEntry(zipEntry); //读取待压缩的文件并写进压缩包里 fis = new FileInputStream(sourceFiles[i]); bis = new BufferedInputStream(fis, 1024*10); int read = 0; while((read=bis.read(bufs, 0, 1024*10)) != -1){ zos.write(bufs,0,read); } } flag = true; } } } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } finally{ //关闭流 try { if(null != bis) bis.close(); if(null != zos) zos.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } return flag; } public static void main(String[] args){ String sourceFilePath = "D:\\TestFile"; String zipFilePath = "D:\\tmp"; String fileName = "12700153file"; boolean flag = FileToZip.fileToZip(sourceFilePath, zipFilePath, fileName); if(flag){ System.out.println("文件打包成功!"); }else{ System.out.println("文件打包失败!"); } } /*** * 获得文件的大小 * */ public static String GetFileSize(File file){ String size = ""; if(file.exists() && file.isFile()){ long fileS = file.length(); DecimalFormat df = new DecimalFormat("#.00"); if (fileS < 1024) { size = df.format((double) fileS) + "BT"; } else if (fileS < 1048576) { size = df.format((double) fileS / 1024) + "KB"; } else if (fileS < 1073741824) { size = df.format((double) fileS / 1048576) + "MB"; } else { size = df.format((double) fileS / 1073741824) +"GB"; } }else if(file.exists() && file.isDirectory()){ size = ""; }else{ size = "0BT"; } return size; } /** * 判断字符是否是中文 * * @param c 字符 * @return 是否是中文 */ public static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { return true; } return false; } /** * 判断字符串是否是乱码 * * @param strName 字符串 * @return 是否是乱码 */ private static boolean isMessyCode(String strName) { try { Pattern p = Pattern.compile("\\s*|\t*|\r*|\n*"); Matcher m = p.matcher(strName); String after = m.replaceAll(""); String temp = after.replaceAll("\\p{P}", ""); char[] ch = temp.trim().toCharArray(); int length = (ch != null) ? ch.length : 0; for (int i = 0; i < length; i++) { char c = ch[i]; if (!Character.isLetterOrDigit(c)) { String str = "" + ch[i]; if (!str.matches("[\u4e00-\u9fa5]+")) { return true; } } } } catch (Exception e) { e.printStackTrace(); } return false; } }
posted on 2017-09-19 10:19 luzhouxiaoshuai 阅读(384) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!