[{"data":1,"prerenderedAt":1757},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage":3,"surround:\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage":1723,"ancestors:\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage":1732,"children:\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage":1737,"related:\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage":1738},{"id":4,"title":5,"body":6,"dateModified":1691,"datePublished":1691,"description":1692,"extension":1693,"faq":1694,"keyword":1703,"kind":1704,"meta":1705,"navigation":358,"path":1713,"seo":1714,"slug":1717,"stem":1718,"topics":1719,"type":1721,"__hash__":1722},"docs\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage\u002Findex.md","Upload an Excel Report to Google Cloud Storage",{"type":7,"value":8,"toc":1677},"minimark",[9,19,24,117,140,317,321,333,430,450,454,457,603,731,736,740,743,943,957,1057,1064,1188,1192,1203,1328,1331,1335,1341,1428,1448,1452,1455,1519,1522,1526,1529,1533,1581,1585,1601,1605,1622,1628,1634,1640,1644,1673],[10,11,12,13,18],"p",{},"When reporting jobs run on Google Cloud — Cloud Run, Cloud Functions, Composer, a GKE CronJob — Cloud Storage is where their output naturally lands: durable, cheap, versionable, and readable by BigQuery, Looker Studio and anyone given access. The client library makes the upload itself one call. The decisions around it are what make a reporting bucket pleasant to live with: how objects are named so that runs do not overwrite each other, what metadata travels with the file, how a large upload is checked, how people without Google Cloud access get the file, and how old reports are cleaned up. This page, part of ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002F","Publishing Excel Reports to Cloud Storage",", goes through them with code that runs unchanged against a local emulator.",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install google-cloud-storage pandas openpyxl\n# for local testing\ndocker run -d --rm -p 4443:4443 fsouza\u002Ffake-gcs-server -scheme http -port 4443\nexport STORAGE_EMULATOR_HOST=http:\u002F\u002F127.0.0.1:4443\n","bash","",[32,33,34,56,63,100],"code",{"__ignoreMap":30},[35,36,39,43,47,50,53],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," google-cloud-storage",[35,51,52],{"class":45}," pandas",[35,54,55],{"class":45}," openpyxl\n",[35,57,59],{"class":37,"line":58},2,[35,60,62],{"class":61},"s-wDw","# for local testing\n",[35,64,66,69,72,76,79,82,85,88,91,94,97],{"class":37,"line":65},3,[35,67,68],{"class":41},"docker",[35,70,71],{"class":45}," run",[35,73,75],{"class":74},"sP0c6"," -d",[35,77,78],{"class":74}," --rm",[35,80,81],{"class":74}," -p",[35,83,84],{"class":45}," 4443:4443",[35,86,87],{"class":45}," fsouza\u002Ffake-gcs-server",[35,89,90],{"class":74}," -scheme",[35,92,93],{"class":45}," http",[35,95,96],{"class":74}," -port",[35,98,99],{"class":74}," 4443\n",[35,101,103,107,111,114],{"class":37,"line":102},4,[35,104,106],{"class":105},"s-kum","export",[35,108,110],{"class":109},"skGVy"," STORAGE_EMULATOR_HOST",[35,112,113],{"class":105},"=",[35,115,116],{"class":109},"http:\u002F\u002F127.0.0.1:4443\n",[10,118,119,120,123,124,127,128,131,132,135,136,139],{},"The code on this page ran against ",[32,121,122],{},"fake-gcs-server",", an open-source Cloud Storage emulator: with ",[32,125,126],{},"STORAGE_EMULATOR_HOST"," set, the client library talks to it instead of Google, and uses anonymous credentials. Unset the variable and the same code talks to the real service. The bucket ",[32,129,130],{},"acme-reports"," exists; ",[32,133,134],{},"weekly_sales.xlsx"," is this week's report and ",[32,137,138],{},"transactions_export.xlsx"," a larger file for the resumable upload.",[141,142,150,151,150,155,150,159,150,167,150,177,150,188,150,195,150,200,150,206,150,211,150,215,150,222,150,228,150,232,150,236,150,240,150,243,150,246,150,252,150,256,150,260,150,263,150,267,150,270,150,275,150,282,150,287,150,290,150,294,150,297,150,302,150,309,150,314],"svg",{"viewBox":143,"role":144,"ariaLabelledBy":145,"xmlns":148,"style":149},"4 0 752 142","img",[146,147],"gcs-flow-t","gcs-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[152,153,154],"title",{"id":146},"From a finished workbook to a governed object",[156,157,158],"desc",{"id":147},"The job authenticates with Application Default Credentials, names the object by report and date, uploads with the right content type and metadata and a no-overwrite precondition, and hands out a time-limited link; a lifecycle rule deletes old reports.",[160,161],"rect",{"x":162,"y":163,"width":164,"height":165,"fill":166},"4","0","752","142","#ffffff",[160,168],{"x":169,"y":170,"width":171,"height":172,"rx":173,"fill":174,"stroke":175,"style":176},"20.0","16.0","115.2","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[178,179,181],"g",{"transform":180},"translate(65.6,37.8) scale(1.000)",[182,183],"path",{"d":184,"fill":185,"stroke":186,"style":187},"M8 11a4 4 0 1 0 .01 0z M11 13l9-9 M17 7l2 2 M15 9l2 2","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[189,190,194],"text",{"x":191,"y":192,"style":193},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Credentials",[189,196,199],{"x":191,"y":197,"style":198},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","ADC, no key file",[37,201],{"x1":202,"y1":203,"x2":204,"y2":203,"stroke":205,"style":176},"140.2","71.2","162.2","var(--brand,#5b5cf0)",[207,208],"polygon",{"points":209,"fill":210},"166.2,71.2 157.2,76.2 157.2,66.2","#5b5cf0",[160,212],{"x":213,"y":170,"width":171,"height":172,"rx":173,"fill":214,"stroke":205,"style":176},"171.2","#f0f4ff",[178,216,218],{"transform":217},"translate(216.8,29.0) scale(1.000)",[182,219],{"d":220,"fill":185,"stroke":221,"style":187},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4","var(--brand-strong,#4338ca)",[189,223,227],{"x":224,"y":225,"style":226},"228.8","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Name by",[189,229,231],{"x":224,"y":230,"style":226},"93.0","date",[189,233,235],{"x":224,"y":234,"style":198},"112.5","sales\u002F2026-09-28\u002F…",[37,237],{"x1":238,"y1":203,"x2":239,"y2":203,"stroke":205,"style":176},"291.4","313.4",[207,241],{"points":242,"fill":210},"317.4,71.2 308.4,76.2 308.4,66.2",[160,244],{"x":245,"y":170,"width":171,"height":172,"rx":173,"fill":214,"stroke":205,"style":176},"322.4",[178,247,249],{"transform":248},"translate(368.0,29.0) scale(1.000)",[182,250],{"d":251,"fill":185,"stroke":221,"style":187},"M7 18h10a4 4 0 0 0 .6-8A6 6 0 0 0 6.1 11.4 3.4 3.4 0 0 0 7 18z",[189,253,255],{"x":254,"y":225,"style":226},"380.0","Upload",[189,257,259],{"x":254,"y":258,"style":198},"95.0","type, metadata,",[189,261,262],{"x":254,"y":234,"style":198},"no overwrite",[37,264],{"x1":265,"y1":203,"x2":266,"y2":203,"stroke":205,"style":176},"442.6","464.6",[207,268],{"points":269,"fill":210},"468.6,71.2 459.6,76.2 459.6,66.2",[160,271],{"x":272,"y":170,"width":171,"height":172,"rx":173,"fill":273,"stroke":274,"style":176},"473.6","#d9f4f1","var(--teal,#0f9488)",[178,276,278],{"transform":277},"translate(519.2,37.8) scale(1.000)",[182,279],{"d":280,"fill":185,"stroke":281,"style":187},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--teal-ink,#0b6157)",[189,283,286],{"x":284,"y":192,"style":285},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Share",[189,288,289],{"x":284,"y":197,"style":198},"signed URL",[37,291],{"x1":292,"y1":203,"x2":293,"y2":203,"stroke":205,"style":176},"593.8","615.8",[207,295],{"points":296,"fill":210},"619.8,71.2 610.8,76.2 610.8,66.2",[160,298],{"x":299,"y":170,"width":171,"height":172,"rx":173,"fill":300,"stroke":301,"style":176},"624.8","#fdefd8","var(--gold,#b4740a)",[178,303,305],{"transform":304},"translate(670.4,37.8) scale(1.000)",[182,306],{"d":307,"fill":185,"stroke":308,"style":187},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","var(--gold-ink,#7a4e06)",[189,310,313],{"x":311,"y":192,"style":312},"682.4","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Retain",[189,315,316],{"x":311,"y":197,"style":198},"lifecycle rule",[20,318,320],{"id":319},"step-1-authenticate-without-key-files","Step 1 — Authenticate without key files",[10,322,323,324,328,329,332],{},"The client finds credentials through ",[325,326,327],"em",{},"Application Default Credentials",": the service account attached to a Cloud Run service or VM, ",[32,330,331],{},"gcloud auth application-default login"," on a developer machine, or workload identity on GKE. No JSON key file needs to be downloaded, stored or rotated:",[25,334,338],{"className":335,"code":336,"language":337,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","from google.cloud import storage\n\nclient = storage.Client(project=\"acme-analytics\")\nbucket = client.bucket(\"acme-reports\")\nprint(type(client._credentials).__name__, [b.name for b in client.list_buckets()])\n","python",[32,339,340,354,360,382,397],{"__ignoreMap":30},[35,341,342,345,348,351],{"class":37,"line":38},[35,343,344],{"class":105},"from",[35,346,347],{"class":109}," google.cloud ",[35,349,350],{"class":105},"import",[35,352,353],{"class":109}," storage\n",[35,355,356],{"class":37,"line":58},[35,357,359],{"emptyLinePlaceholder":358},true,"\n",[35,361,362,365,367,370,374,376,379],{"class":37,"line":65},[35,363,364],{"class":109},"client ",[35,366,113],{"class":105},[35,368,369],{"class":109}," storage.Client(",[35,371,373],{"class":372},"sa561","project",[35,375,113],{"class":105},[35,377,378],{"class":45},"\"acme-analytics\"",[35,380,381],{"class":109},")\n",[35,383,384,387,389,392,395],{"class":37,"line":102},[35,385,386],{"class":109},"bucket ",[35,388,113],{"class":105},[35,390,391],{"class":109}," client.bucket(",[35,393,394],{"class":45},"\"acme-reports\"",[35,396,381],{"class":109},[35,398,400,403,406,409,412,415,418,421,424,427],{"class":37,"line":399},5,[35,401,402],{"class":74},"print",[35,404,405],{"class":109},"(",[35,407,408],{"class":74},"type",[35,410,411],{"class":109},"(client._credentials).",[35,413,414],{"class":74},"__name__",[35,416,417],{"class":109},", [b.name ",[35,419,420],{"class":105},"for",[35,422,423],{"class":109}," b ",[35,425,426],{"class":105},"in",[35,428,429],{"class":109}," client.list_buckets()])\n",[10,431,432,433,436,437,440,441,444,445,449],{},"Against the emulator the credentials are anonymous; on Google Cloud they are the job's service account, which needs a role that allows creating objects in the bucket — ",[32,434,435],{},"roles\u002Fstorage.objectCreator"," is enough to upload, plus ",[32,438,439],{},"objectViewer"," if the job also reads. Key files still work (",[32,442,443],{},"GOOGLE_APPLICATION_CREDENTIALS=\u002Fpath\u002Fkey.json",") but are the thing security teams most want to eliminate; see ",[14,446,448],{"href":447},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Fstore-credentials-for-excel-report-scripts-safely\u002F","Store Credentials for Excel Report Scripts Safely",".",[20,451,453],{"id":452},"step-2-name-objects-by-report-and-date","Step 2 — Name objects by report and date",[10,455,456],{},"Cloud Storage has no folders — only object names with slashes in them, which the console displays as folders. Put the report name and the reporting date in the name, so every run gets its own object and listing a prefix gives the history:",[25,458,460],{"className":335,"code":459,"language":337,"meta":30,"style":30},"from datetime import date\n\ndef object_name(report, run_date, filename):\n    return f\"{report}\u002F{run_date:%Y-%m-%d}\u002F{filename}\"\n\nname = object_name(\"sales\", date(2026, 9, 28), \"weekly_sales.xlsx\")\nprint(name)\n",[32,461,462,474,478,490,552,556,595],{"__ignoreMap":30},[35,463,464,466,469,471],{"class":37,"line":38},[35,465,344],{"class":105},[35,467,468],{"class":109}," datetime ",[35,470,350],{"class":105},[35,472,473],{"class":109}," date\n",[35,475,476],{"class":37,"line":58},[35,477,359],{"emptyLinePlaceholder":358},[35,479,480,483,487],{"class":37,"line":65},[35,481,482],{"class":105},"def",[35,484,486],{"class":485},"s_Opv"," object_name",[35,488,489],{"class":109},"(report, run_date, filename):\n",[35,491,492,495,498,501,505,508,511,514,516,519,522,525,528,530,533,535,538,540,542,544,547,549],{"class":37,"line":102},[35,493,494],{"class":105},"    return",[35,496,497],{"class":105}," f",[35,499,500],{"class":45},"\"",[35,502,504],{"class":503},"sSjpA","{",[35,506,507],{"class":109},"report",[35,509,510],{"class":503},"}",[35,512,513],{"class":45},"\u002F",[35,515,504],{"class":503},[35,517,518],{"class":109},"run_date",[35,520,521],{"class":105},":",[35,523,524],{"class":109},"%",[35,526,527],{"class":105},"Y-",[35,529,524],{"class":109},[35,531,532],{"class":105},"m-",[35,534,524],{"class":109},[35,536,537],{"class":105},"d",[35,539,510],{"class":503},[35,541,513],{"class":45},[35,543,504],{"class":503},[35,545,546],{"class":109},"filename",[35,548,510],{"class":503},[35,550,551],{"class":45},"\"\n",[35,553,554],{"class":37,"line":399},[35,555,359],{"emptyLinePlaceholder":358},[35,557,559,562,564,567,570,573,576,579,582,584,587,590,593],{"class":37,"line":558},6,[35,560,561],{"class":109},"name ",[35,563,113],{"class":105},[35,565,566],{"class":109}," object_name(",[35,568,569],{"class":45},"\"sales\"",[35,571,572],{"class":109},", date(",[35,574,575],{"class":74},"2026",[35,577,578],{"class":109},", ",[35,580,581],{"class":74},"9",[35,583,578],{"class":109},[35,585,586],{"class":74},"28",[35,588,589],{"class":109},"), ",[35,591,592],{"class":45},"\"weekly_sales.xlsx\"",[35,594,381],{"class":109},[35,596,598,600],{"class":37,"line":597},7,[35,599,402],{"class":74},[35,601,602],{"class":109},"(name)\n",[141,604,150,609,150,612,150,615,150,618,150,622,150,627,150,631,150,635,150,640,150,646,150,652,150,657,150,660,150,663,150,666,150,671,150,675,150,679,150,682,150,685,150,688,150,693,150,698,150,702,150,705,150,708,150,711,150,716,150,721,150,725,150,728],{"viewBox":605,"role":144,"ariaLabelledBy":606,"xmlns":148,"style":149},"4 0 752 436",[607,608],"gcs-names-t","gcs-names-d",[152,610,611],{"id":607},"How dated object names look in the bucket",[156,613,614],{"id":608},"The acme-reports bucket fans out to objects named by report and date. Each weekly sales run has its own dated prefix, a separate latest copy is optional, and the transactions export lives under its own report prefix.",[160,616],{"x":162,"y":163,"width":164,"height":617,"fill":166},"436",[160,619],{"x":169,"y":620,"width":621,"height":230,"rx":173,"fill":174,"stroke":175,"style":176},"171.5","220.0",[178,623,625],{"transform":624},"translate(118.0,184.5) scale(1.000)",[182,626],{"d":251,"fill":185,"stroke":186,"style":187},[189,628,130],{"x":629,"y":630,"style":193},"130.0","231.0",[189,632,634],{"x":629,"y":633,"style":198},"250.5","one bucket for all reports",[160,636],{"x":637,"y":170,"width":638,"height":639,"rx":173,"fill":214,"stroke":205,"style":176},"490.0","250.0","92.0",[178,641,643],{"transform":642},"translate(603.0,29.0) scale(1.000)",[182,644],{"d":645,"fill":185,"stroke":221,"style":187},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6",[189,647,651],{"x":648,"y":649,"style":650},"615.0","75.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","sales\u002F2026-09-21\u002Fweekly_sales.xlsx",[189,653,656],{"x":648,"y":654,"style":655},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","last week",[182,658],{"d":659,"fill":185,"stroke":205,"style":176},"M244.0,218.0 C364.0,218.0 364.0,62.0 480.0,62.0",[207,661],{"points":662,"fill":210},"484.0,62.0 475.0,67.0 475.0,57.0",[160,664],{"x":637,"y":665,"width":638,"height":639,"rx":173,"fill":214,"stroke":205,"style":176},"120.0",[178,667,669],{"transform":668},"translate(603.0,133.0) scale(1.000)",[182,670],{"d":645,"fill":185,"stroke":221,"style":187},[189,672,674],{"x":648,"y":673,"style":650},"179.0","sales\u002F2026-09-28\u002Fweekly_sales.xlsx",[189,676,678],{"x":648,"y":677,"style":655},"198.0","this week",[182,680],{"d":681,"fill":185,"stroke":205,"style":176},"M244.0,218.0 C364.0,218.0 364.0,166.0 480.0,166.0",[207,683],{"points":684,"fill":210},"484.0,166.0 475.0,171.0 475.0,161.0",[160,686],{"x":637,"y":687,"width":638,"height":639,"rx":173,"fill":273,"stroke":274,"style":176},"224.0",[178,689,691],{"transform":690},"translate(603.0,237.0) scale(1.000)",[182,692],{"d":280,"fill":185,"stroke":281,"style":187},[189,694,697],{"x":648,"y":695,"style":696},"283.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","sales\u002Flatest\u002Fweekly_sales.xlsx",[189,699,701],{"x":648,"y":700,"style":655},"302.0","optional fixed name",[182,703],{"d":704,"fill":185,"stroke":205,"style":176},"M244.0,218.0 C364.0,218.0 364.0,270.0 480.0,270.0",[207,706],{"points":707,"fill":210},"484.0,270.0 475.0,275.0 475.0,265.0",[160,709],{"x":637,"y":710,"width":638,"height":639,"rx":173,"fill":300,"stroke":301,"style":176},"328.0",[178,712,714],{"transform":713},"translate(603.0,341.0) scale(1.000)",[182,715],{"d":645,"fill":185,"stroke":308,"style":187},[189,717,720],{"x":648,"y":718,"style":719},"387.0","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","transactions\u002F2026-09-28\u002F…",[189,722,724],{"x":648,"y":723,"style":655},"406.0","another report",[182,726],{"d":727,"fill":185,"stroke":205,"style":176},"M244.0,218.0 C364.0,218.0 364.0,374.0 480.0,374.0",[207,729],{"points":730,"fill":210},"484.0,374.0 475.0,379.0 475.0,369.0",[10,732,733,734,449],{},"Dated names make reruns and backfills safe — a rerun for last Monday writes to last Monday's name — and make \"the file from four weeks ago\" a matter of listing. A fixed name that is overwritten every week is simpler for people who bookmark it; if you need both, write the dated object and also copy it to ",[32,735,697],{},[20,737,739],{"id":738},"step-3-upload-with-content-type-metadata-and-a-precondition","Step 3 — Upload with content type, metadata and a precondition",[10,741,742],{},"Set the content type explicitly, attach metadata describing where the file came from, and add a precondition so an accidental second run cannot overwrite a report that was already published:",[25,744,746],{"className":335,"code":745,"language":337,"meta":30,"style":30},"XLSX = \"application\u002Fvnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n\ndef publish(bucket, local_path, name, metadata, overwrite=False):\n    blob = bucket.blob(name)\n    blob.metadata = metadata\n    blob.upload_from_filename(local_path, content_type=XLSX, checksum=\"md5\",\n                              if_generation_match=None if overwrite else 0)\n    return bucket.get_blob(name)\n\nmeta = {\"report\": \"weekly-sales\", \"source\": \"erp-export\", \"rows\": \"4\"}\nblob = publish(bucket, \"weekly_sales.xlsx\", name, meta)\nprint(blob.name, blob.size, blob.content_type)\nprint(blob.metadata, \"md5\", blob.md5_hash)\n",[32,747,748,759,763,781,791,801,826,850,858,863,906,922,930],{"__ignoreMap":30},[35,749,750,753,756],{"class":37,"line":38},[35,751,752],{"class":74},"XLSX",[35,754,755],{"class":105}," =",[35,757,758],{"class":45}," \"application\u002Fvnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n",[35,760,761],{"class":37,"line":58},[35,762,359],{"emptyLinePlaceholder":358},[35,764,765,767,770,773,775,778],{"class":37,"line":65},[35,766,482],{"class":105},[35,768,769],{"class":485}," publish",[35,771,772],{"class":109},"(bucket, local_path, name, metadata, overwrite",[35,774,113],{"class":105},[35,776,777],{"class":74},"False",[35,779,780],{"class":109},"):\n",[35,782,783,786,788],{"class":37,"line":102},[35,784,785],{"class":109},"    blob ",[35,787,113],{"class":105},[35,789,790],{"class":109}," bucket.blob(name)\n",[35,792,793,796,798],{"class":37,"line":399},[35,794,795],{"class":109},"    blob.metadata ",[35,797,113],{"class":105},[35,799,800],{"class":109}," metadata\n",[35,802,803,806,809,811,813,815,818,820,823],{"class":37,"line":558},[35,804,805],{"class":109},"    blob.upload_from_filename(local_path, ",[35,807,808],{"class":372},"content_type",[35,810,113],{"class":105},[35,812,752],{"class":74},[35,814,578],{"class":109},[35,816,817],{"class":372},"checksum",[35,819,113],{"class":105},[35,821,822],{"class":45},"\"md5\"",[35,824,825],{"class":109},",\n",[35,827,828,831,833,836,839,842,845,848],{"class":37,"line":597},[35,829,830],{"class":372},"                              if_generation_match",[35,832,113],{"class":105},[35,834,835],{"class":74},"None",[35,837,838],{"class":105}," if",[35,840,841],{"class":109}," overwrite ",[35,843,844],{"class":105},"else",[35,846,847],{"class":74}," 0",[35,849,381],{"class":109},[35,851,853,855],{"class":37,"line":852},8,[35,854,494],{"class":105},[35,856,857],{"class":109}," bucket.get_blob(name)\n",[35,859,861],{"class":37,"line":860},9,[35,862,359],{"emptyLinePlaceholder":358},[35,864,866,869,871,874,877,880,883,885,888,890,893,895,898,900,903],{"class":37,"line":865},10,[35,867,868],{"class":109},"meta ",[35,870,113],{"class":105},[35,872,873],{"class":109}," {",[35,875,876],{"class":45},"\"report\"",[35,878,879],{"class":109},": ",[35,881,882],{"class":45},"\"weekly-sales\"",[35,884,578],{"class":109},[35,886,887],{"class":45},"\"source\"",[35,889,879],{"class":109},[35,891,892],{"class":45},"\"erp-export\"",[35,894,578],{"class":109},[35,896,897],{"class":45},"\"rows\"",[35,899,879],{"class":109},[35,901,902],{"class":45},"\"4\"",[35,904,905],{"class":109},"}\n",[35,907,909,912,914,917,919],{"class":37,"line":908},11,[35,910,911],{"class":109},"blob ",[35,913,113],{"class":105},[35,915,916],{"class":109}," publish(bucket, ",[35,918,592],{"class":45},[35,920,921],{"class":109},", name, meta)\n",[35,923,925,927],{"class":37,"line":924},12,[35,926,402],{"class":74},[35,928,929],{"class":109},"(blob.name, blob.size, blob.content_type)\n",[35,931,933,935,938,940],{"class":37,"line":932},13,[35,934,402],{"class":74},[35,936,937],{"class":109},"(blob.metadata, ",[35,939,822],{"class":45},[35,941,942],{"class":109},", blob.md5_hash)\n",[10,944,945,948,949,952,953,956],{},[32,946,947],{},"if_generation_match=0"," means \"only if no object with this name exists\"; a second attempt fails with a ",[32,950,951],{},"412 Precondition Failed"," instead of silently replacing the published file. ",[32,954,955],{},"checksum=\"md5\""," makes the library compare the checksum the service computed with the local file and raise if they differ, so a corrupted upload never passes as success. The explicit content type means browsers and download tools open the file as a workbook rather than guessing from the name.",[25,958,960],{"className":335,"code":959,"language":337,"meta":30,"style":30},"from google.api_core.exceptions import PreconditionFailed\n\ntry:\n    publish(bucket, \"weekly_sales.xlsx\", name, meta)\nexcept PreconditionFailed as err:\n    print(\"already published:\", err.code)\nblob = publish(bucket, \"weekly_sales.xlsx\", name, meta, overwrite=True)\nprint(\"replaced deliberately, generation\", blob.generation)\n",[32,961,962,974,978,986,995,1009,1022,1045],{"__ignoreMap":30},[35,963,964,966,969,971],{"class":37,"line":38},[35,965,344],{"class":105},[35,967,968],{"class":109}," google.api_core.exceptions ",[35,970,350],{"class":105},[35,972,973],{"class":109}," PreconditionFailed\n",[35,975,976],{"class":37,"line":58},[35,977,359],{"emptyLinePlaceholder":358},[35,979,980,983],{"class":37,"line":65},[35,981,982],{"class":105},"try",[35,984,985],{"class":109},":\n",[35,987,988,991,993],{"class":37,"line":102},[35,989,990],{"class":109},"    publish(bucket, ",[35,992,592],{"class":45},[35,994,921],{"class":109},[35,996,997,1000,1003,1006],{"class":37,"line":399},[35,998,999],{"class":105},"except",[35,1001,1002],{"class":109}," PreconditionFailed ",[35,1004,1005],{"class":105},"as",[35,1007,1008],{"class":109}," err:\n",[35,1010,1011,1014,1016,1019],{"class":37,"line":558},[35,1012,1013],{"class":74},"    print",[35,1015,405],{"class":109},[35,1017,1018],{"class":45},"\"already published:\"",[35,1020,1021],{"class":109},", err.code)\n",[35,1023,1024,1026,1028,1030,1032,1035,1038,1040,1043],{"class":37,"line":597},[35,1025,911],{"class":109},[35,1027,113],{"class":105},[35,1029,916],{"class":109},[35,1031,592],{"class":45},[35,1033,1034],{"class":109},", name, meta, ",[35,1036,1037],{"class":372},"overwrite",[35,1039,113],{"class":105},[35,1041,1042],{"class":74},"True",[35,1044,381],{"class":109},[35,1046,1047,1049,1051,1054],{"class":37,"line":852},[35,1048,402],{"class":74},[35,1050,405],{"class":109},[35,1052,1053],{"class":45},"\"replaced deliberately, generation\"",[35,1055,1056],{"class":109},", blob.generation)\n",[10,1058,1059,1060,1063],{},"A deliberate rerun passes ",[32,1061,1062],{},"overwrite=True",". With object versioning enabled on the bucket, the replaced version is kept as a noncurrent version, so even deliberate overwrites can be undone.",[141,1065,150,1070,150,1073,150,1076,150,1079,150,1083,150,1088,150,1091,150,1095,150,1099,150,1102,150,1105,150,1113,150,1118,150,1122,150,1128,150,1132,150,1136,150,1138,150,1141,150,1145,150,1148,150,1151,150,1157,150,1159,150,1162,150,1164,150,1167,150,1171,150,1174,150,1177,150,1183,150,1185],{"viewBox":1066,"role":144,"ariaLabelledBy":1067,"xmlns":148,"style":149},"4 0 752 272",[1068,1069],"gcs-precondition-t","gcs-precondition-d",[152,1071,1072],{"id":1068},"What if_generation_match=0 does on upload",[156,1074,1075],{"id":1069},"A decision diagram. If no object with the name exists the upload succeeds and creates generation one; if one exists the service rejects the upload with 412 Precondition Failed; a deliberate rerun omits the precondition and replaces the object.",[160,1077],{"x":162,"y":163,"width":164,"height":1078,"fill":166},"272",[160,1080],{"x":1081,"y":170,"width":1082,"height":225,"rx":173,"fill":214,"stroke":205,"style":176},"240.0","280.0",[178,1084,1085],{"transform":248},[182,1086],{"d":1087,"fill":185,"stroke":221,"style":187},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5",[189,1089,1090],{"x":254,"y":225,"style":226},"Does the object already exist?",[37,1092],{"x1":254,"y1":1093,"x2":254,"y2":1094,"stroke":205,"style":176},"91.5","111.5",[37,1096],{"x1":1097,"y1":1094,"x2":1098,"y2":1094,"stroke":205,"style":176},"134.7","625.3",[37,1100],{"x1":1097,"y1":1094,"x2":1097,"y2":1101,"stroke":205,"style":176},"157.5",[207,1103],{"points":1104,"fill":210},"134.7,161.5 129.7,152.5 139.7,152.5",[160,1106],{"x":1107,"y":1108,"width":1109,"height":1110,"rx":1111,"fill":166,"stroke":175,"style":1112},"118.2","121.5","32.9","21.0","10","stroke-width:1.2px",[189,1114,1117],{"x":1097,"y":1115,"style":1116},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","no",[160,1119],{"x":169,"y":1120,"width":1121,"height":230,"rx":173,"fill":273,"stroke":274,"style":176},"163.5","229.3",[178,1123,1125],{"transform":1124},"translate(122.7,176.5) scale(1.000)",[182,1126],{"d":1127,"fill":185,"stroke":281,"style":187},"M5 12.5l4.5 4.5L19 7",[189,1129,1131],{"x":1097,"y":1130,"style":285},"223.0","Created",[189,1133,1135],{"x":1097,"y":1134,"style":198},"242.5","new generation",[37,1137],{"x1":254,"y1":1094,"x2":254,"y2":1101,"stroke":205,"style":176},[207,1139],{"points":1140,"fill":210},"380.0,161.5 375.0,152.5 385.0,152.5",[160,1142],{"x":1143,"y":1108,"width":1144,"height":1110,"rx":1111,"fill":166,"stroke":175,"style":1112},"360.4","39.2",[189,1146,1147],{"x":254,"y":1115,"style":1116},"yes",[160,1149],{"x":1150,"y":1120,"width":1121,"height":230,"rx":173,"fill":300,"stroke":301,"style":176},"265.3",[178,1152,1154],{"transform":1153},"translate(368.0,176.5) scale(1.000)",[182,1155],{"d":1156,"fill":185,"stroke":308,"style":187},"M6 11h12v9H6z M8.5 11V8a3.5 3.5 0 0 1 7 0v3",[189,1158,951],{"x":254,"y":1130,"style":312},[189,1160,1161],{"x":254,"y":1134,"style":198},"published file untouched",[37,1163],{"x1":1098,"y1":1094,"x2":1098,"y2":1101,"stroke":205,"style":176},[207,1165],{"points":1166,"fill":210},"625.3,161.5 620.3,152.5 630.3,152.5",[160,1168],{"x":1169,"y":1108,"width":1170,"height":1110,"rx":1111,"fill":166,"stroke":175,"style":1112},"569.0","112.6",[189,1172,1173],{"x":1098,"y":1115,"style":1116},"deliberate rerun",[160,1175],{"x":1176,"y":1120,"width":1121,"height":230,"rx":173,"fill":214,"stroke":205,"style":176},"510.7",[178,1178,1180],{"transform":1179},"translate(613.3,176.5) scale(1.000)",[182,1181],{"d":1182,"fill":185,"stroke":221,"style":187},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[189,1184,1062],{"x":1098,"y":1130,"style":226},[189,1186,1187],{"x":1098,"y":1134,"style":198},"replaces the object",[20,1189,1191],{"id":1190},"step-4-large-files-resumable-checksummed-uploads","Step 4 — Large files: resumable, checksummed uploads",[10,1193,1194,1195,1198,1199,1202],{},"Files above a few megabytes are uploaded in chunks through a ",[325,1196,1197],{},"resumable"," upload, which can pick up where it left off after a network error. The library does this automatically for large files; set ",[32,1200,1201],{},"chunk_size"," to control it — it must be a multiple of 256 KB:",[25,1204,1206],{"className":335,"code":1205,"language":337,"meta":30,"style":30},"big = bucket.blob(object_name(\"transactions\", date(2026, 9, 28), \"transactions_export.xlsx\"))\nbig.chunk_size = 1024 * 1024\nbig.upload_from_filename(\"transactions_export.xlsx\", content_type=XLSX, checksum=\"md5\", if_generation_match=0)\nstored = bucket.get_blob(big.name)\nprint(stored.name, f\"{stored.size:,} bytes\")\n",[32,1207,1208,1241,1257,1291,1301],{"__ignoreMap":30},[35,1209,1210,1213,1215,1218,1221,1223,1225,1227,1229,1231,1233,1235,1238],{"class":37,"line":38},[35,1211,1212],{"class":109},"big ",[35,1214,113],{"class":105},[35,1216,1217],{"class":109}," bucket.blob(object_name(",[35,1219,1220],{"class":45},"\"transactions\"",[35,1222,572],{"class":109},[35,1224,575],{"class":74},[35,1226,578],{"class":109},[35,1228,581],{"class":74},[35,1230,578],{"class":109},[35,1232,586],{"class":74},[35,1234,589],{"class":109},[35,1236,1237],{"class":45},"\"transactions_export.xlsx\"",[35,1239,1240],{"class":109},"))\n",[35,1242,1243,1246,1248,1251,1254],{"class":37,"line":58},[35,1244,1245],{"class":109},"big.chunk_size ",[35,1247,113],{"class":105},[35,1249,1250],{"class":74}," 1024",[35,1252,1253],{"class":105}," *",[35,1255,1256],{"class":74}," 1024\n",[35,1258,1259,1262,1264,1266,1268,1270,1272,1274,1276,1278,1280,1282,1285,1287,1289],{"class":37,"line":65},[35,1260,1261],{"class":109},"big.upload_from_filename(",[35,1263,1237],{"class":45},[35,1265,578],{"class":109},[35,1267,808],{"class":372},[35,1269,113],{"class":105},[35,1271,752],{"class":74},[35,1273,578],{"class":109},[35,1275,817],{"class":372},[35,1277,113],{"class":105},[35,1279,822],{"class":45},[35,1281,578],{"class":109},[35,1283,1284],{"class":372},"if_generation_match",[35,1286,113],{"class":105},[35,1288,163],{"class":74},[35,1290,381],{"class":109},[35,1292,1293,1296,1298],{"class":37,"line":102},[35,1294,1295],{"class":109},"stored ",[35,1297,113],{"class":105},[35,1299,1300],{"class":109}," bucket.get_blob(big.name)\n",[35,1302,1303,1305,1308,1311,1313,1315,1318,1321,1323,1326],{"class":37,"line":399},[35,1304,402],{"class":74},[35,1306,1307],{"class":109},"(stored.name, ",[35,1309,1310],{"class":105},"f",[35,1312,500],{"class":45},[35,1314,504],{"class":503},[35,1316,1317],{"class":109},"stored.size",[35,1319,1320],{"class":105},":,",[35,1322,510],{"class":503},[35,1324,1325],{"class":45}," bytes\"",[35,1327,381],{"class":109},[10,1329,1330],{},"The 3 MB file went up in three 1 MB chunks and was checksummed as a whole once the last chunk arrived. Smaller chunks mean less to resend after a failure and more requests overall; 8–16 MB suits large exports on a reliable network.",[20,1332,1334],{"id":1333},"step-5-give-people-a-time-limited-link","Step 5 — Give people a time-limited link",[10,1336,1337,1338,1340],{},"Colleagues without Google Cloud access can download a report through a ",[325,1339,289],{},": a link that carries a signature granting read access to one object until it expires. Signing needs a service-account identity, so it does not work with anonymous emulator credentials — the code is shown as it runs in production:",[25,1342,1344],{"className":335,"code":1343,"language":337,"meta":30,"style":30},"from datetime import timedelta\n\nurl = bucket.blob(name).generate_signed_url(version=\"v4\", expiration=timedelta(days=7), method=\"GET\",\n                                             response_disposition='attachment; filename=\"weekly_sales.xlsx\"')\nprint(url)\n",[32,1345,1346,1357,1361,1409,1421],{"__ignoreMap":30},[35,1347,1348,1350,1352,1354],{"class":37,"line":38},[35,1349,344],{"class":105},[35,1351,468],{"class":109},[35,1353,350],{"class":105},[35,1355,1356],{"class":109}," timedelta\n",[35,1358,1359],{"class":37,"line":58},[35,1360,359],{"emptyLinePlaceholder":358},[35,1362,1363,1366,1368,1371,1374,1376,1379,1381,1384,1386,1389,1392,1394,1397,1399,1402,1404,1407],{"class":37,"line":65},[35,1364,1365],{"class":109},"url ",[35,1367,113],{"class":105},[35,1369,1370],{"class":109}," bucket.blob(name).generate_signed_url(",[35,1372,1373],{"class":372},"version",[35,1375,113],{"class":105},[35,1377,1378],{"class":45},"\"v4\"",[35,1380,578],{"class":109},[35,1382,1383],{"class":372},"expiration",[35,1385,113],{"class":105},[35,1387,1388],{"class":109},"timedelta(",[35,1390,1391],{"class":372},"days",[35,1393,113],{"class":105},[35,1395,1396],{"class":74},"7",[35,1398,589],{"class":109},[35,1400,1401],{"class":372},"method",[35,1403,113],{"class":105},[35,1405,1406],{"class":45},"\"GET\"",[35,1408,825],{"class":109},[35,1410,1411,1414,1416,1419],{"class":37,"line":102},[35,1412,1413],{"class":372},"                                             response_disposition",[35,1415,113],{"class":105},[35,1417,1418],{"class":45},"'attachment; filename=\"weekly_sales.xlsx\"'",[35,1420,381],{"class":109},[35,1422,1423,1425],{"class":37,"line":399},[35,1424,402],{"class":74},[35,1426,1427],{"class":109},"(url)\n",[10,1429,1430,1431,1434,1435,1438,1439,1442,1443,1447],{},"Version 4 signed URLs can be valid for at most seven days. On Cloud Run or Compute Engine, where the credentials have no private key, pass ",[32,1432,1433],{},"service_account_email"," and an ",[32,1436,1437],{},"access_token"," so the library signs through the IAM API instead. ",[32,1440,1441],{},"response_disposition"," makes the browser download the file with a clean name instead of showing the long object path. Emailing the link rather than the file keeps large reports out of mailboxes; ",[14,1444,1446],{"href":1445},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-an-excel-report-with-gmail\u002F","Send an Excel Report with Gmail"," covers sending it.",[20,1449,1451],{"id":1450},"step-6-delete-old-reports-automatically","Step 6 — Delete old reports automatically",[10,1453,1454],{},"A reporting bucket grows forever unless something deletes old objects. A lifecycle rule on the bucket does it without any code running: delete objects older than a set age, or only those under a prefix:",[25,1456,1458],{"className":335,"code":1457,"language":337,"meta":30,"style":30},"bucket = client.get_bucket(\"acme-reports\")\nbucket.add_lifecycle_delete_rule(age=400, matches_prefix=[\"sales\u002F\"])\nbucket.patch()\nprint(list(bucket.lifecycle_rules))\n",[32,1459,1460,1473,1502,1507],{"__ignoreMap":30},[35,1461,1462,1464,1466,1469,1471],{"class":37,"line":38},[35,1463,386],{"class":109},[35,1465,113],{"class":105},[35,1467,1468],{"class":109}," client.get_bucket(",[35,1470,394],{"class":45},[35,1472,381],{"class":109},[35,1474,1475,1478,1481,1483,1486,1488,1491,1493,1496,1499],{"class":37,"line":58},[35,1476,1477],{"class":109},"bucket.add_lifecycle_delete_rule(",[35,1479,1480],{"class":372},"age",[35,1482,113],{"class":105},[35,1484,1485],{"class":74},"400",[35,1487,578],{"class":109},[35,1489,1490],{"class":372},"matches_prefix",[35,1492,113],{"class":105},[35,1494,1495],{"class":109},"[",[35,1497,1498],{"class":45},"\"sales\u002F\"",[35,1500,1501],{"class":109},"])\n",[35,1503,1504],{"class":37,"line":65},[35,1505,1506],{"class":109},"bucket.patch()\n",[35,1508,1509,1511,1513,1516],{"class":37,"line":102},[35,1510,402],{"class":74},[35,1512,405],{"class":109},[35,1514,1515],{"class":74},"list",[35,1517,1518],{"class":109},"(bucket.lifecycle_rules))\n",[10,1520,1521],{},"Four hundred days keeps a year of weekly reports plus a margin for year-on-year comparisons. The emulator used for this page accepts the call but does not store lifecycle rules, so this step was not exercised locally — check the bucket's lifecycle configuration in the console after applying it.",[20,1523,1525],{"id":1524},"performance-notes","Performance notes",[10,1527,1528],{},"A weekly workbook of a few megabytes uploads in about a second from inside Google Cloud; most of the time is TLS setup and authentication, so reuse one client for every upload in a run rather than creating one per file. For many small files — one workbook per region or per customer — upload them in parallel from a small pool of workers, giving each worker its own client. Uploads from outside Google Cloud are limited by the local connection, and the resumable protocol is what keeps a slow or flaky link from restarting a large file from zero.",[20,1530,1532],{"id":1531},"common-pitfalls","Common pitfalls",[1534,1535,1536,1544,1550,1560,1569],"ul",{},[1537,1538,1539,1543],"li",{},[1540,1541,1542],"strong",{},"Downloading service-account keys."," Use Application Default Credentials; keys are a liability.",[1537,1545,1546,1549],{},[1540,1547,1548],{},"One fixed object name for every run."," Reruns overwrite history; name objects by report and date.",[1537,1551,1552,1555,1556,1559],{},[1540,1553,1554],{},"No content type."," Browsers may treat the file as generic binary; set the ",[32,1557,1558],{},".xlsx"," type explicitly.",[1537,1561,1562,1565,1566,1568],{},[1540,1563,1564],{},"Silent overwrites."," Use ",[32,1567,947],{}," for publish-once objects.",[1537,1570,1571,1574,1575,1577,1578,1580],{},[1540,1572,1573],{},"Signed URLs from credentials without a key."," Sign through IAM with ",[32,1576,1433],{}," and ",[32,1579,1437],{}," on Cloud Run and GCE.",[20,1582,1584],{"id":1583},"conclusion","Conclusion",[10,1586,1587,1588,1590,1591,1577,1593,1595,1596,1598,1599,449],{},"Let Application Default Credentials authenticate the job, name each object by report and date, and upload with the ",[32,1589,1558],{}," content type, descriptive metadata, ",[32,1592,955],{},[32,1594,947],{}," so published reports are never overwritten by accident. Large files go up as resumable uploads in chunk multiples of 256 KB, people outside Google Cloud get a signed URL valid for up to seven days, and a lifecycle rule removes old reports. Test all of it locally with ",[32,1597,122],{}," by setting ",[32,1600,126],{},[20,1602,1604],{"id":1603},"frequently-asked-questions","Frequently asked questions",[10,1606,1607,1610,1611,1614,1615,1618,1619,1621],{},[1540,1608,1609],{},"How do I upload an Excel file to Google Cloud Storage with Python?","\nInstall google-cloud-storage, create ",[32,1612,1613],{},"storage.Client()",", get the bucket, and call ",[32,1616,1617],{},"upload_from_filename"," on a blob with the path and the xlsx MIME type as ",[32,1620,808],{},". Credentials come from Application Default Credentials.",[10,1623,1624,1627],{},[1540,1625,1626],{},"How do I stop an upload from overwriting an existing file?","\nPass if_generation_match=0. The upload then succeeds only if no object with that name exists, and otherwise fails with 412 Precondition Failed.",[10,1629,1630,1633],{},[1540,1631,1632],{},"How can I share a Cloud Storage file with someone without Google access?","\nGenerate a V4 signed URL for the object with an expiration of up to seven days. Anyone with the link can download the file until it expires.",[10,1635,1636,1639],{},[1540,1637,1638],{},"How do I test Cloud Storage code locally?","\nRun the fake-gcs-server emulator in Docker and set STORAGE_EMULATOR_HOST to its address. The client library then sends requests to the emulator with anonymous credentials.",[20,1641,1643],{"id":1642},"related","Related",[1534,1645,1646,1652,1659,1666],{},[1537,1647,1648,1649,1651],{},"Up one level: ",[14,1650,17],{"href":16}," — every destination for finished reports.",[1537,1653,1654,1658],{},[14,1655,1657],{"href":1656},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-amazon-s3-with-boto3\u002F","Upload an Excel Report to Amazon S3 with boto3"," — the AWS equivalent.",[1537,1660,1661,1665],{},[14,1662,1664],{"href":1663},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-an-excel-report-on-google-cloud-run-jobs\u002F","Run an Excel Report on Google Cloud Run Jobs"," — where GCS uploads usually run.",[1537,1667,1668,1672],{},[14,1669,1671],{"href":1670},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-azure-blob-storage\u002F","Upload an Excel Report to Azure Blob Storage"," — the Azure equivalent.",[1674,1675,1676],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":58,"depth":58,"links":1678},[1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690],{"id":22,"depth":58,"text":23},{"id":319,"depth":58,"text":320},{"id":452,"depth":58,"text":453},{"id":738,"depth":58,"text":739},{"id":1190,"depth":58,"text":1191},{"id":1333,"depth":58,"text":1334},{"id":1450,"depth":58,"text":1451},{"id":1524,"depth":58,"text":1525},{"id":1531,"depth":58,"text":1532},{"id":1583,"depth":58,"text":1584},{"id":1603,"depth":58,"text":1604},{"id":1642,"depth":58,"text":1643},"2026-09-29","Publish generated workbooks to a Cloud Storage bucket from Python: authentication without key files, dated object names, the correct content type and metadata, uploads that refuse to overwrite, checksummed resumable uploads for large files, time-limited download links, retention rules, and local testing against an emulator.","md",[1695,1697,1699,1701],{"q":1609,"a":1696},"Install google-cloud-storage, create storage.Client(), get the bucket, and call upload_from_filename on a blob with the path and the xlsx MIME type as content_type. Credentials come from Application Default Credentials.",{"q":1626,"a":1698},"Pass if_generation_match=0. The upload then succeeds only if no object with that name exists, and otherwise fails with 412 Precondition Failed.",{"q":1632,"a":1700},"Generate a V4 signed URL for the object with an expiration of up to seven days. Anyone with the link can download the file until it expires.",{"q":1638,"a":1702},"Run the fake-gcs-server emulator in Docker and set STORAGE_EMULATOR_HOST to its address. The client library then sends requests to the emulator with anonymous credentials.","upload an excel report to google cloud storage",null,{"breadcrumb":1706},[1707,1709,1712],{"name":1708,"item":513},"Home",{"name":1710,"item":1711},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":17,"item":16},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage",{"title":1715,"description":1716},"Upload Excel Files to Google Cloud Storage with Python","Upload an Excel report to a GCS bucket with google-cloud-storage: Application Default Credentials, blob naming by date, xlsx content type and metadata, if_generation_match to prevent overwrites, resumable uploads with MD5 checks, signed URLs and lifecycle rules, tested with fake-gcs-server.","upload-an-excel-report-to-google-cloud-storage","automating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-google-cloud-storage\u002Findex",[1720],"publishing","how-to","wiP1TFm7bGvF249du4hmCmQewWD8jd9WSVTMLkQVua8",[1724,1728],{"title":1725,"path":1726,"stem":1727,"children":-1},"Upload an Excel Report to Dropbox with Python","\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-dropbox","automating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-dropbox\u002Findex",{"title":1729,"path":1730,"stem":1731,"children":-1},"Upload an Excel Report to SharePoint with Python","\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-sharepoint-with-python","automating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-sharepoint-with-python\u002Findex",[1733,1735],{"path":1734,"title":1710},"\u002Fautomating-reporting-workflows",{"path":1736,"title":17},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage",[],[1739,1742,1745,1748,1751,1754],{"path":1740,"title":1741,"kind":1721},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-an-excel-file-from-sharepoint-or-onedrive-into-pandas","Read an Excel File from SharePoint or OneDrive into pandas",{"path":1743,"title":1744,"kind":1721},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fshare-an-excel-file-in-onedrive-with-a-link-using-graph","Share an Excel File in OneDrive with a Link Using Graph",{"path":1746,"title":1747,"kind":1721},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Femail-a-download-link-instead-of-an-excel-attachment","Email a Download Link Instead of an Excel Attachment",{"path":1749,"title":1750,"kind":1721},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-an-excel-report-with-amazon-ses","Send an Excel Report with Amazon SES",{"path":1752,"title":1753,"kind":1721},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-an-html-report-from-excel-data-with-jinja2","Generate an HTML Report from Excel Data with Jinja2",{"path":1755,"title":1756,"kind":1721},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fname-version-and-archive-generated-excel-reports","Name, Version and Archive Generated Excel Reports",1790972138515]