[{"data":1,"prerenderedAt":1154},["ShallowReactive",2],{"docs-/docs/development/files":3},{"id":4,"title":5,"body":6,"description":1147,"extension":1148,"meta":1149,"navigation":273,"path":1150,"seo":1151,"stem":1152,"__hash__":1153},"docs/docs/development/files.md","Files",{"type":7,"value":8,"toc":1136},"minimark",[9,13,44,62,67,74,82,103,107,117,226,242,253,259,295,304,309,320,329,332,351,355,368,424,435,463,468,477,484,494,517,521,532,643,647,650,797,865,876,880,905,1085,1089,1092,1129,1132],[10,11,5],"h1",{"id":12},"files",[14,15,16,17,21,22,34,35,39,40,43],"p",{},"A ",[18,19,20],"code",{},"file"," field lets your tool take a file from the user or hand one back. The important thing to understand up front: ",[23,24,25,26,29,30,33],"strong",{},"file bytes never travel inside the ",[18,27,28],{},"input","/",[18,31,32],{},"output"," JSON",". Only ",[36,37,38],"em",{},"filenames"," do. The actual bytes live in a per-tool ",[23,41,42],{},"files directory"," on the sandbox, and your code reads and writes them through the SDK.",[14,45,46,47,52,53,56,57,61],{},"This page covers the full round-trip. For declaring the field itself, see ",[48,49,51],"a",{"href":50},"/docs/development/fields","Fields","; for the ",[18,54,55],{},"process"," contract, see ",[48,58,60],{"href":59},"/docs/development/entry-point","Entry point",".",[63,64,66],"h2",{"id":65},"the-files-directory","The files directory",[14,68,69,70,73],{},"Every tool gets its own files directory on the sandbox. Uploaded inputs land there, and anything you write there can be served back to the browser. You never need to know its absolute path — the SDK resolves it for you from the ",[18,71,72],{},"ARTIFUNCS_FILES_DIR"," environment variable.",[14,75,76,77,81],{},"In ",[48,78,80],{"href":79},"/docs/sandbox/overview","anonymous / multi-tenant sandboxes",", every visitor session gets its own separate files directory, so two people running the same tool never see each other's files. That isolation is handled for you — as long as you go through the SDK rather than hardcoding paths.",[83,84,85],"blockquote",{},[14,86,87,88,91,92,95,96,99,100,61],{},"Filenames are always reduced to their ",[23,89,90],{},"base name"," (path components are stripped). You can't read or write into subdirectories, and ",[18,93,94],{},"../"," traversal is blocked. Pass plain names like ",[18,97,98],{},"\"report.pdf\"",", not ",[18,101,102],{},"\"out/report.pdf\"",[63,104,106],{"id":105},"file-inputs","File inputs",[14,108,109,110,112,113,116],{},"Declare a ",[18,111,20],{}," input field in ",[18,114,115],{},"artifuncs.json",":",[118,119,124],"pre",{"className":120,"code":121,"language":122,"meta":123,"style":123},"language-json shiki shiki-themes github-light github-dark","\"fields\": {\n  \"input\": {\n    \"upload\": {\n      \"type\": \"file\",\n      \"label\": \"Upload CSV\",\n      \"accept\": \".csv\",\n      \"maxSize\": 5242880\n    }\n  }\n}\n","json","",[18,125,126,139,148,156,171,184,197,208,214,220],{"__ignoreMap":123},[127,128,131,135],"span",{"class":129,"line":130},"line",1,[127,132,134],{"class":133},"sZZnC","\"fields\"",[127,136,138],{"class":137},"sVt8B",": {\n",[127,140,142,146],{"class":129,"line":141},2,[127,143,145],{"class":144},"sj4cs","  \"input\"",[127,147,138],{"class":137},[127,149,151,154],{"class":129,"line":150},3,[127,152,153],{"class":144},"    \"upload\"",[127,155,138],{"class":137},[127,157,159,162,165,168],{"class":129,"line":158},4,[127,160,161],{"class":144},"      \"type\"",[127,163,164],{"class":137},": ",[127,166,167],{"class":133},"\"file\"",[127,169,170],{"class":137},",\n",[127,172,174,177,179,182],{"class":129,"line":173},5,[127,175,176],{"class":144},"      \"label\"",[127,178,164],{"class":137},[127,180,181],{"class":133},"\"Upload CSV\"",[127,183,170],{"class":137},[127,185,187,190,192,195],{"class":129,"line":186},6,[127,188,189],{"class":144},"      \"accept\"",[127,191,164],{"class":137},[127,193,194],{"class":133},"\".csv\"",[127,196,170],{"class":137},[127,198,200,203,205],{"class":129,"line":199},7,[127,201,202],{"class":144},"      \"maxSize\"",[127,204,164],{"class":137},[127,206,207],{"class":144},"5242880\n",[127,209,211],{"class":129,"line":210},8,[127,212,213],{"class":137},"    }\n",[127,215,217],{"class":129,"line":216},9,[127,218,219],{"class":137},"  }\n",[127,221,223],{"class":129,"line":222},10,[127,224,225],{"class":137},"}\n",[14,227,228,229,232,233,235,236,238,239,116],{},"When the user picks a file, the browser uploads it to the sandbox ",[23,230,231],{},"before"," the run starts. By the time ",[18,234,55],{}," is called, the file is already on disk and the value you receive in ",[18,237,28],{}," is just its ",[23,240,241],{},"filename as a string",[118,243,247],{"className":244,"code":245,"language":246,"meta":123,"style":123},"language-python shiki shiki-themes github-light github-dark","input == { \"upload\": \"data.csv\" }\n","python",[18,248,249],{"__ignoreMap":123},[127,250,251],{"class":129,"line":130},[127,252,245],{},[14,254,255,256,116],{},"Read its bytes with ",[18,257,258],{},"get_file_content",[118,260,262],{"className":244,"code":261,"language":246,"meta":123,"style":123},"from artifuncs.sandbox_sdk import get_file_content\n\ndef process(input, settings):\n    raw = get_file_content(input[\"upload\"])   # -> bytes\n    rows = raw.decode(\"utf-8\").splitlines()\n    return { \"count\": len(rows) }\n",[18,263,264,269,275,280,285,290],{"__ignoreMap":123},[127,265,266],{"class":129,"line":130},[127,267,268],{},"from artifuncs.sandbox_sdk import get_file_content\n",[127,270,271],{"class":129,"line":141},[127,272,274],{"emptyLinePlaceholder":273},true,"\n",[127,276,277],{"class":129,"line":150},[127,278,279],{},"def process(input, settings):\n",[127,281,282],{"class":129,"line":158},[127,283,284],{},"    raw = get_file_content(input[\"upload\"])   # -> bytes\n",[127,286,287],{"class":129,"line":173},[127,288,289],{},"    rows = raw.decode(\"utf-8\").splitlines()\n",[127,291,292],{"class":129,"line":186},[127,293,294],{},"    return { \"count\": len(rows) }\n",[14,296,297,299,300,303],{},[18,298,258],{}," returns the raw ",[18,301,302],{},"bytes",". Decode them yourself if you want text.",[305,306,308],"h3",{"id":307},"multiple-files","Multiple files",[14,310,311,312,315,316,319],{},"With ",[18,313,314],{},"\"multiple\": true",", the field value is a ",[23,317,318],{},"list of filename strings"," instead of a single one:",[118,321,323],{"className":244,"code":322,"language":246,"meta":123,"style":123},"input == { \"uploads\": [\"data.csv\", \"extra.csv\"] }\n",[18,324,325],{"__ignoreMap":123},[127,326,327],{"class":129,"line":130},[127,328,322],{},[14,330,331],{},"Iterate over the list and read each name the same way:",[118,333,335],{"className":244,"code":334,"language":246,"meta":123,"style":123},"def process(input, settings):\n    total = sum(len(get_file_content(name)) for name in input[\"uploads\"])\n    return { \"total_bytes\": total }\n",[18,336,337,341,346],{"__ignoreMap":123},[127,338,339],{"class":129,"line":130},[127,340,279],{},[127,342,343],{"class":129,"line":141},[127,344,345],{},"    total = sum(len(get_file_content(name)) for name in input[\"uploads\"])\n",[127,347,348],{"class":129,"line":150},[127,349,350],{},"    return { \"total_bytes\": total }\n",[63,352,354],{"id":353},"file-outputs","File outputs",[14,356,109,357,359,360,363,364,367],{},[18,358,20],{}," field under ",[18,361,362],{},"fields.output"," — same ",[18,365,366],{},"type",", just in the output block:",[118,369,371],{"className":120,"code":370,"language":122,"meta":123,"style":123},"\"fields\": {\n  \"output\": {\n    \"result\": {\n      \"type\": \"file\",\n      \"label\": \"Processed file\"\n    }\n  }\n}\n",[18,372,373,379,386,393,403,412,416,420],{"__ignoreMap":123},[127,374,375,377],{"class":129,"line":130},[127,376,134],{"class":133},[127,378,138],{"class":137},[127,380,381,384],{"class":129,"line":141},[127,382,383],{"class":144},"  \"output\"",[127,385,138],{"class":137},[127,387,388,391],{"class":129,"line":150},[127,389,390],{"class":144},"    \"result\"",[127,392,138],{"class":137},[127,394,395,397,399,401],{"class":129,"line":158},[127,396,161],{"class":144},[127,398,164],{"class":137},[127,400,167],{"class":133},[127,402,170],{"class":137},[127,404,405,407,409],{"class":129,"line":173},[127,406,176],{"class":144},[127,408,164],{"class":137},[127,410,411],{"class":133},"\"Processed file\"\n",[127,413,414],{"class":129,"line":186},[127,415,213],{"class":137},[127,417,418],{"class":129,"line":199},[127,419,219],{"class":137},[127,421,422],{"class":129,"line":210},[127,423,225],{"class":137},[14,425,426,427,430,431,434],{},"Write the bytes with ",[18,428,429],{},"save_file_content",", then ",[23,432,433],{},"return what it gives you"," under the output field's name:",[118,436,438],{"className":244,"code":437,"language":246,"meta":123,"style":123},"from artifuncs.sandbox_sdk import save_file_content\n\ndef process(input, settings):\n    pdf_bytes = build_pdf(...)\n    return { \"result\": save_file_content(\"report.pdf\", pdf_bytes) }\n",[18,439,440,445,449,453,458],{"__ignoreMap":123},[127,441,442],{"class":129,"line":130},[127,443,444],{},"from artifuncs.sandbox_sdk import save_file_content\n",[127,446,447],{"class":129,"line":141},[127,448,274],{"emptyLinePlaceholder":273},[127,450,451],{"class":129,"line":150},[127,452,279],{},[127,454,455],{"class":129,"line":158},[127,456,457],{},"    pdf_bytes = build_pdf(...)\n",[127,459,460],{"class":129,"line":173},[127,461,462],{},"    return { \"result\": save_file_content(\"report.pdf\", pdf_bytes) }\n",[14,464,465,467],{},[18,466,429],{}," writes the file to the files directory and returns a small dict:",[118,469,471],{"className":244,"code":470,"language":246,"meta":123,"style":123},"{ \"path\": \"report.pdf\", \"size\": 20481 }\n",[18,472,473],{"__ignoreMap":123},[127,474,475],{"class":129,"line":130},[127,476,470],{},[14,478,479,480,483],{},"That ",[18,481,482],{},"{ path, size }"," shape is exactly what a file-output field expects. The UI turns it into a download button — clicking it fetches the file back from the sandbox by name. If you return the wrong shape (e.g. just the byte string), the output renders empty.",[14,485,486,487,490,491,493],{},"If the file is already on disk — for example you wrote it some other way, or you're echoing back an input — use ",[18,488,489],{},"get_file_info"," to produce the same ",[18,492,482],{}," dict without re-reading the bytes:",[118,495,497],{"className":244,"code":496,"language":246,"meta":123,"style":123},"from artifuncs.sandbox_sdk import get_file_info\n\ndef process(input, settings):\n    return { \"result\": get_file_info(input[\"upload\"]) }\n",[18,498,499,504,508,512],{"__ignoreMap":123},[127,500,501],{"class":129,"line":130},[127,502,503],{},"from artifuncs.sandbox_sdk import get_file_info\n",[127,505,506],{"class":129,"line":141},[127,507,274],{"emptyLinePlaceholder":273},[127,509,510],{"class":129,"line":150},[127,511,279],{},[127,513,514],{"class":129,"line":158},[127,515,516],{},"    return { \"result\": get_file_info(input[\"upload\"]) }\n",[63,518,520],{"id":519},"sdk-reference","SDK reference",[14,522,523,524,527,528,531],{},"All helpers import from ",[18,525,526],{},"artifuncs.sandbox_sdk"," and operate on the tool's files directory. They raise ",[18,529,530],{},"RuntimeError"," if called outside a sandbox.",[533,534,535,551],"table",{},[536,537,538],"thead",{},[539,540,541,545,548],"tr",{},[542,543,544],"th",{},"Function",[542,546,547],{},"Returns",[542,549,550],{},"Notes",[552,553,554,573,594,613,628],"tbody",{},[539,555,556,562,566],{},[557,558,559],"td",{},[18,560,561],{},"get_file_content(filename)",[557,563,564],{},[18,565,302],{},[557,567,568,569,572],{},"Raw file content. Raises ",[18,570,571],{},"FileNotFoundError"," if missing.",[539,574,575,580,585],{},[557,576,577],{},[18,578,579],{},"save_file_content(filename, content)",[557,581,582],{},[18,583,584],{},"{ \"path\", \"size\" }",[557,586,587,590,591,593],{},[18,588,589],{},"content"," must be ",[18,592,302],{},". Creates the files directory if needed. Return this for a file-output field.",[539,595,596,601,605],{},[557,597,598],{},[18,599,600],{},"get_file_info(filename)",[557,602,603],{},[18,604,584],{},[557,606,607,608,610,611,572],{},"Same shape as ",[18,609,429],{},", but for a file already on disk. Raises ",[18,612,571],{},[539,614,615,620,625],{},[557,616,617],{},[18,618,619],{},"list_files()",[557,621,622],{},[18,623,624],{},"list[str]",[557,626,627],{},"Filenames currently in the directory.",[539,629,630,635,640],{},[557,631,632],{},[18,633,634],{},"file_exists(filename)",[557,636,637],{},[18,638,639],{},"bool",[557,641,642],{},"Cheap existence check.",[63,644,646],{"id":645},"end-to-end-example","End-to-end example",[14,648,649],{},"A tool that takes an uploaded image and returns a thumbnail:",[118,651,653],{"className":120,"code":652,"language":122,"meta":123,"style":123},"\"fields\": {\n  \"input\": {\n    \"image\": { \"type\": \"file\", \"label\": \"Source image\", \"accept\": \"image/*\" }\n  },\n  \"output\": {\n    \"thumb\": { \"type\": \"file\", \"label\": \"Thumbnail\" }\n  }\n},\n\"settings\": {\n  \"size\": { \"type\": \"number\", \"label\": \"Max dimension\", \"default\": 256 }\n}\n",[18,654,655,661,667,706,711,717,741,745,750,757,792],{"__ignoreMap":123},[127,656,657,659],{"class":129,"line":130},[127,658,134],{"class":133},[127,660,138],{"class":137},[127,662,663,665],{"class":129,"line":141},[127,664,145],{"class":144},[127,666,138],{"class":137},[127,668,669,672,675,678,680,682,685,688,690,693,695,698,700,703],{"class":129,"line":150},[127,670,671],{"class":144},"    \"image\"",[127,673,674],{"class":137},": { ",[127,676,677],{"class":144},"\"type\"",[127,679,164],{"class":137},[127,681,167],{"class":133},[127,683,684],{"class":137},", ",[127,686,687],{"class":144},"\"label\"",[127,689,164],{"class":137},[127,691,692],{"class":133},"\"Source image\"",[127,694,684],{"class":137},[127,696,697],{"class":144},"\"accept\"",[127,699,164],{"class":137},[127,701,702],{"class":133},"\"image/*\"",[127,704,705],{"class":137}," }\n",[127,707,708],{"class":129,"line":158},[127,709,710],{"class":137},"  },\n",[127,712,713,715],{"class":129,"line":173},[127,714,383],{"class":144},[127,716,138],{"class":137},[127,718,719,722,724,726,728,730,732,734,736,739],{"class":129,"line":186},[127,720,721],{"class":144},"    \"thumb\"",[127,723,674],{"class":137},[127,725,677],{"class":144},[127,727,164],{"class":137},[127,729,167],{"class":133},[127,731,684],{"class":137},[127,733,687],{"class":144},[127,735,164],{"class":137},[127,737,738],{"class":133},"\"Thumbnail\"",[127,740,705],{"class":137},[127,742,743],{"class":129,"line":199},[127,744,219],{"class":137},[127,746,747],{"class":129,"line":210},[127,748,749],{"class":137},"},\n",[127,751,752,755],{"class":129,"line":216},[127,753,754],{"class":133},"\"settings\"",[127,756,138],{"class":137},[127,758,759,762,764,766,768,771,773,775,777,780,782,785,787,790],{"class":129,"line":222},[127,760,761],{"class":144},"  \"size\"",[127,763,674],{"class":137},[127,765,677],{"class":144},[127,767,164],{"class":137},[127,769,770],{"class":133},"\"number\"",[127,772,684],{"class":137},[127,774,687],{"class":144},[127,776,164],{"class":137},[127,778,779],{"class":133},"\"Max dimension\"",[127,781,684],{"class":137},[127,783,784],{"class":144},"\"default\"",[127,786,164],{"class":137},[127,788,789],{"class":144},"256",[127,791,705],{"class":137},[127,793,795],{"class":129,"line":794},11,[127,796,225],{"class":137},[118,798,800],{"className":244,"code":799,"language":246,"meta":123,"style":123},"import io\nfrom PIL import Image\nfrom artifuncs.sandbox_sdk import get_file_content, save_file_content, log\n\ndef process(input, settings):\n    img = Image.open(io.BytesIO(get_file_content(input[\"image\"])))\n    img.thumbnail((settings[\"size\"], settings[\"size\"]))\n\n    buf = io.BytesIO()\n    img.save(buf, format=\"PNG\")\n    log.info(f\"thumbnail {img.size}\")\n\n    return { \"thumb\": save_file_content(\"thumb.png\", buf.getvalue()) }\n",[18,801,802,807,812,817,821,825,830,835,839,844,849,854,859],{"__ignoreMap":123},[127,803,804],{"class":129,"line":130},[127,805,806],{},"import io\n",[127,808,809],{"class":129,"line":141},[127,810,811],{},"from PIL import Image\n",[127,813,814],{"class":129,"line":150},[127,815,816],{},"from artifuncs.sandbox_sdk import get_file_content, save_file_content, log\n",[127,818,819],{"class":129,"line":158},[127,820,274],{"emptyLinePlaceholder":273},[127,822,823],{"class":129,"line":173},[127,824,279],{},[127,826,827],{"class":129,"line":186},[127,828,829],{},"    img = Image.open(io.BytesIO(get_file_content(input[\"image\"])))\n",[127,831,832],{"class":129,"line":199},[127,833,834],{},"    img.thumbnail((settings[\"size\"], settings[\"size\"]))\n",[127,836,837],{"class":129,"line":210},[127,838,274],{"emptyLinePlaceholder":273},[127,840,841],{"class":129,"line":216},[127,842,843],{},"    buf = io.BytesIO()\n",[127,845,846],{"class":129,"line":222},[127,847,848],{},"    img.save(buf, format=\"PNG\")\n",[127,850,851],{"class":129,"line":794},[127,852,853],{},"    log.info(f\"thumbnail {img.size}\")\n",[127,855,857],{"class":129,"line":856},12,[127,858,274],{"emptyLinePlaceholder":273},[127,860,862],{"class":129,"line":861},13,[127,863,864],{},"    return { \"thumb\": save_file_content(\"thumb.png\", buf.getvalue()) }\n",[14,866,867,868,871,872,875],{},"(Pillow goes in ",[18,869,870],{},"requirements.txt"," — see ",[48,873,60],{"href":874},"/docs/development/entry-point#dependencies",".)",[63,877,879],{"id":878},"javascript-tools","JavaScript tools",[14,881,882,883,886,887,890,891,894,895,898,899,902,903,61],{},"The file SDK helpers above are ",[23,884,885],{},"Python-only",". JavaScript tools get logging via ",[18,888,889],{},"require('artifuncs-log')"," but handle files directly with Node's ",[18,892,893],{},"fs",", reading the files directory from ",[18,896,897],{},"process.env.ARTIFUNCS_FILES_DIR",". The contract is the same: ",[18,900,901],{},"input.field"," is a filename, and a file-output field expects ",[18,904,482],{},[118,906,910],{"className":907,"code":908,"language":909,"meta":123,"style":123},"language-js shiki shiki-themes github-light github-dark","import { readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport function process(input, settings) {\n    const dir = process.env.ARTIFUNCS_FILES_DIR\n    const raw = readFileSync(join(dir, input.upload))\n\n    const out = 'result.txt'\n    const content = Buffer.from(raw.toString().toUpperCase())\n    writeFileSync(join(dir, out), content)\n\n    return { result: { path: out, size: content.length } }\n}\n","js",[18,911,912,927,939,943,969,986,1006,1010,1022,1051,1063,1067,1081],{"__ignoreMap":123},[127,913,914,918,921,924],{"class":129,"line":130},[127,915,917],{"class":916},"szBVR","import",[127,919,920],{"class":137}," { readFileSync, writeFileSync } ",[127,922,923],{"class":916},"from",[127,925,926],{"class":133}," 'node:fs'\n",[127,928,929,931,934,936],{"class":129,"line":141},[127,930,917],{"class":916},[127,932,933],{"class":137}," { join } ",[127,935,923],{"class":916},[127,937,938],{"class":133}," 'node:path'\n",[127,940,941],{"class":129,"line":150},[127,942,274],{"emptyLinePlaceholder":273},[127,944,945,948,951,955,958,961,963,966],{"class":129,"line":158},[127,946,947],{"class":916},"export",[127,949,950],{"class":916}," function",[127,952,954],{"class":953},"sScJk"," process",[127,956,957],{"class":137},"(",[127,959,28],{"class":960},"s4XuR",[127,962,684],{"class":137},[127,964,965],{"class":960},"settings",[127,967,968],{"class":137},") {\n",[127,970,971,974,977,980,983],{"class":129,"line":173},[127,972,973],{"class":916},"    const",[127,975,976],{"class":144}," dir",[127,978,979],{"class":916}," =",[127,981,982],{"class":137}," process.env.",[127,984,985],{"class":144},"ARTIFUNCS_FILES_DIR\n",[127,987,988,990,993,995,998,1000,1003],{"class":129,"line":186},[127,989,973],{"class":916},[127,991,992],{"class":144}," raw",[127,994,979],{"class":916},[127,996,997],{"class":953}," readFileSync",[127,999,957],{"class":137},[127,1001,1002],{"class":953},"join",[127,1004,1005],{"class":137},"(dir, input.upload))\n",[127,1007,1008],{"class":129,"line":199},[127,1009,274],{"emptyLinePlaceholder":273},[127,1011,1012,1014,1017,1019],{"class":129,"line":210},[127,1013,973],{"class":916},[127,1015,1016],{"class":144}," out",[127,1018,979],{"class":916},[127,1020,1021],{"class":133}," 'result.txt'\n",[127,1023,1024,1026,1029,1031,1034,1036,1039,1042,1045,1048],{"class":129,"line":216},[127,1025,973],{"class":916},[127,1027,1028],{"class":144}," content",[127,1030,979],{"class":916},[127,1032,1033],{"class":137}," Buffer.",[127,1035,923],{"class":953},[127,1037,1038],{"class":137},"(raw.",[127,1040,1041],{"class":953},"toString",[127,1043,1044],{"class":137},"().",[127,1046,1047],{"class":953},"toUpperCase",[127,1049,1050],{"class":137},"())\n",[127,1052,1053,1056,1058,1060],{"class":129,"line":222},[127,1054,1055],{"class":953},"    writeFileSync",[127,1057,957],{"class":137},[127,1059,1002],{"class":953},[127,1061,1062],{"class":137},"(dir, out), content)\n",[127,1064,1065],{"class":129,"line":794},[127,1066,274],{"emptyLinePlaceholder":273},[127,1068,1069,1072,1075,1078],{"class":129,"line":856},[127,1070,1071],{"class":916},"    return",[127,1073,1074],{"class":137}," { result: { path: out, size: content.",[127,1076,1077],{"class":144},"length",[127,1079,1080],{"class":137}," } }\n",[127,1082,1083],{"class":129,"line":861},[127,1084,225],{"class":137},[63,1086,1088],{"id":1087},"what-gets-sent-over-the-wire","What gets sent over the wire",[14,1090,1091],{},"To recap the round-trip:",[1093,1094,1095,1103,1112,1118,1123],"ol",{},[1096,1097,1098,1099,1102],"li",{},"The browser ",[23,1100,1101],{},"uploads"," the chosen file to the sandbox before the run.",[1096,1104,1105,1107,1108,1111],{},[18,1106,55],{}," receives the ",[23,1109,1110],{},"filename",", not the bytes.",[1096,1113,1114,1115,1117],{},"Your code reads the bytes from the files directory (via the SDK in Python, ",[18,1116,893],{}," in JS).",[1096,1119,1120,1121,61],{},"Your code writes any output file to the same directory and returns ",[18,1122,482],{},[1096,1124,1098,1125,1128],{},[23,1126,1127],{},"downloads"," the output file back from the sandbox by name when the user clicks the file-output's download button.",[14,1130,1131],{},"Because only filenames cross the boundary, large files don't bloat the input/output payload — they move as plain HTTP uploads and downloads.",[1133,1134,1135],"style",{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":123,"searchDepth":141,"depth":141,"links":1137},[1138,1139,1142,1143,1144,1145,1146],{"id":65,"depth":141,"text":66},{"id":105,"depth":141,"text":106,"children":1140},[1141],{"id":307,"depth":150,"text":308},{"id":353,"depth":141,"text":354},{"id":519,"depth":141,"text":520},{"id":645,"depth":141,"text":646},{"id":878,"depth":141,"text":879},{"id":1087,"depth":141,"text":1088},"How file inputs and file outputs work — filenames travel in the JSON, bytes live in the sandbox, and the SDK reads and writes them.","md",{},"/docs/development/files",{"title":5,"description":1147},"docs/development/files","aJqJg5ohpJ4tN_pmyva2Xm5se-1CubUtBEn7xOD3N4Y",1781004913741]